在老早以前就想要去接触了解这一块的知识了,奈何在工作中一直都没有机会去接触,就迟迟没有去学习这一块的知识。到现在终于开始去学习了,简单的搜索了一下,都没有太明确的博客去学习,都是很零碎的,而且在最新的版本中也有问题,到最后只能去看官方的文档,当然这才正确的选择,其实一开始就该去看的。废话不多说,这一个系列因为我才开始学习,不知道会有多长,但是会将常用的东西全部总结学习完。
首先来学习一下官方的配置。
一、必要的下载
这一条没啥说的,就是下载一些工具方便开发,当然是在Android Studio中。
对于ndk的开发,需要下载:
- NDK(Native Development Kit):用来让你调用c或者c++代码的工具
- cmake:方便在gradle中构建的,不再使用以前的ndk-build(比较麻烦)
- LLDB:可以调试c或者c++代码的
这三个都可以直接在SDK Manager中去下载(当然也可以手动下载配置好)。目前我使用的版本对应分别是:
NDK : 17.1.4828580
cmake : 3.6.4111459
LLDB : 3.1.4508709
二、在项目中配置NDK
这一点分为两种情况:
- 新建项目时配置
- 在已有项目中配置
第一种情况没啥说的,就是在新建项目时,选中第一个界面的include C++ support
这个选择框,之后一直默认next(最后finish)即可。
重点是第二种情况,因为大多数在在项目一开始时可能没有想着要使用ndk开发功能,后来需要了,那么就去配置即可。
1、创建cpp文件夹
在已有项目的main目录下创建cpp文件夹
2、创建一个c文件
在cpp文件夹下创建一个c文件,右键->new->c/c++ Source File,例如取名叫做Hello,后缀名为.c,不创建.h文件,点击ok。
注:内部暂时可以不用写代码。
3、创建CMakeLists.txt
在当前module中创建一个CMakeLists.txt文件,右键->new->File,取名叫做CMakeLists.txt
,点击ok。加上以下代码:
cmake_minimum_required(VERSION 3.4.1)
add_library( # Specifies the name of the library.
Hello
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/Hello.c )
add_library方法:
- 第一个参数:Hello,就是lib的名称,之后在gradle中的ndk ModuleName一致
- 第二个参数暂时不管
- 第三个参数要与上文中创建的c文件路径和名字完全相同,后缀相同
4、使用cmake配置gradle关联
选择Android视图,在需要构建的module下,右键->Link C++ Project with Gradle->选择CMakeLists.txt的路径,就是上文中创建的CMakeLists.txt,点击ok。
构建完成,可以看到在当前module下的build.gradle文件中多了以下这段话:
externalNativeBuild {
cmake {
path 'CMakeLists.txt'
}
}
这时候再在当前的build.gradle中,android模块,defaultConfig下,添加以下代码:
ndk{
moduleName 'Hello'
}
这里的moduleName就是要与CMakeLists.txt中定义的libraryName一致,这里加入叫Hello吧。
到此基本配置就算是完成了。下面就开始写代码,先从java调用c开始。
三、Java调用C代码
1、定义Java方法
到这一步,终于回到熟悉的Java代码,我们在对应的包名中创建一个类,例如取名叫做Hello,加入一个简单的本地方法,只比普通抽象方法多了一个native关键字,如下:
public native String stringFromC();
2、定义C方法
这时候这个方法是找不到的,还需要在之前的Hello.c文件中写上对应的方法,如下:
#include<jni.h>
jstring Java_net_arvin_androidstudy_jni_Hello_stringFromC(JNIEnv *env, jobject instance) {
return (*env)->NewStringUTF(env, "I am from c!");
}
代码不长,以此来解释,首先必须要引入的是jni.h
,下边的JNIEnv和jstring,jobject等都是在这里边去定义的。
函数的申明部分,jstring
表示在java中定义的String,这个位置也就是返回值,然后Java_net_arvin_androidstudy_jni_Hello_stringFromC
这一部分看起来很长其实就是三个部分:Java_类的完全限定名_方法名,只是完全限定名中的点改为了下划线,后边的两个参数env和instance,env是一个环境,用它可以操作Java类,创建字符串之类的;instance就是当前调用这个代码的类的实例,这里就是Hello类的实例。
最后就是函数的实现内容,这里是返回一个字符串然后,看一看env这个变量的类型,JNIEnv的定义可以看到是一个typedef const struct JNINativeInterface* JNIEnv;
。本身就是指针,然后在方法中的env又是指针,相当于二级指针,所以调用的时候使用的就是(*env)->函数。
3、Java代码与C代码关联
回到Hello类中,在类中加一段静态初始化的代码:
static {
System.loadLibrary("Hello");
}
其中Hello是上文中在build.gradle中ndk下配置的moduleName。
到这里就可以在其他地方创建Hello类的实例,然后调用stringFromC的方法了。
例如:
Hello jni = new Hello();
System.out.println(jni.stringFromC());
就会在run tab下看到I am from c!
这样一句话的输出。
至此Android JNI开发系列之配置就告一段落了。当然后边肯定还有进一步的配置学习,例如需要引入多个c文件,需要引用其他c++库等,肯定也是需要配置的,那些都会在之后的文章中讲到。
感谢
借鉴官方教程