概述
JNI:Java Native Interface,即Java本地接口,使Java与其他类型的语言进行交互,和C/C++交互的比较多。JNI属于Java的一部分,是JDK的组成部分,和Android关系不大,但是Android中核心的业务或高性能的功能都是C/C++开发的,比如游戏渲染、音视频编解码等,所以对于Android开发,JNI也是需要了解的。Android中并不是直接和C/C++打交道,是和.so(动态库),.a(静态库)文件打交道。
创建
Android中使用JNI需要提前安装NDK,NDK(Native Development Kit)是后台Android平台提供的开发工具包,把JNI、gcc、g++…等都封装进入了,方便开发者开发。
project:
CMakeLists.txt:
native-lib.cpp
和常规的Android项目比,大部分都一样,有些配置文件不同,还多了cpp目录,cpp目录下native-lib.cpp文件就是Java调用C/C++的地方。
CMakeLists.txt是CMake的组态档,组态档是用一种建构软件专用的特殊编程语言写的CMake脚本,CMake是CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。
使用
规则
java中的属性和方法在C/C++中不能直接使用,需要按照一定的规则进行转换。
java | C/C++ |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
void | V |
object obj | Lobj |
int[] | [I |
void name(double b) | (D) V |
加载库
// Used to load the 'jni' library on application startup.
static {
System.loadLibrary("jni");
}
如果没有设置库名称,可以直接使用文件名。
static {
System.loadLibrary("native-lib");
}
创建native方法
在MainActivity中创建native方法。
/**
* A native method that is implemented by the 'jni' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
native-lib.cpp中需要使用c++实现创建的native方法。
实现native方法
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jni_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
细节解读:
- extern “C”
表示下面的代码都会采用C的编译方式,因为native-lib是c++文件,所以在c++文件中使用C需要单独标记一下。如果代码使用C++写的就不用使用这句话。 - JNIEXPORT
JNI关键字,表示该方法可以被外部调用,不同的操作系统可能不同。 - jstring
表示该方法的返回值是java中的String类型,若java中返回值为int,则这里为jint,若没有返回值,这里为void。 - JNICALL
JNI关键字,可以没有,是约束函数入栈顺序,栈和堆内存清理规则的。 - Java_com_example_jni_MainActivity_stringFromJNI
Java语言哪个包下,哪个类的哪个方法。 - JNIEnv
c和java之间相互调用的桥梁,通过它相关的操作才能起作用。 - jobject
java中传递过来的对象,相当于Java中的this,这里表示MainActivity。 - NewStringUTF
c中的属性java也不能直接使用,所以需要单独的方法进行转换,这里是转换成java可以使用的字符串。
C中修改Java的字段
Java:
private String name;
// native方法,修改name字段
public native void updateName();
native-lib.cpp中:
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_MainActivity_updateName(JNIEnv *env, jobject thiz) {
// 拿到MainActivity
jclass mainActivityCls = env->GetObjectClass(thiz);
// 获取属性名称及类型
jfieldID name_fid = env->GetFieldID(mainActivityCls,"name", "Ljava/lang/String;");
// 设置被修改的Java的属性的值
jstring value = env->NewStringUTF("JNI测试");
// 赋值
env->SetObjectField(thiz,name_fid,value);
}
- jclass表示MainActivity.class
- jfieldID表示name字段
- “Ljava/lang/String;”表示name类型为String类型
- jstring,表示创建字符串
- SetObjectField,赋值
整个流程走完和反射十分相似,一步一步将需要修改的内容取出来,然后进行赋值。
调用Java方法
Java:
private int num01 = 1;
private int num02 = 2;
// java方法
public int add(int num1,int num2){
return num1 + num2;
}
// native方法
public native int addNum();
native-lib.cpp:
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_jni_MainActivity_addNum(JNIEnv *env, jobject thiz) {
// 拿到MainActivity.class
jclass mainActivityCls = env->GetObjectClass(thiz);
// 获取方法
jmethodID add_mid = env->GetMethodID(mainActivityCls,"add", "(II)I");
// 调用方法
env->CallIntMethod(thiz,add_mid,100,200);
}
- GetMethodID,获取java方法。
- “add”,需要调用的java方法的方法名。
- “(II)I”,两个参数都为int,且返回值也为int。
- CallIntMethod,调用java方法。
- 100,200,调用java方法需要的参数。