1.NDK与JNI的区别:
NDK是为了方便开发基于JNI的应用而提供的一套开发和编译工具集;
JNI是一套编程接口,可以运用在应用层,应用框架层,用以实现Java代码与本地代码的交互。
2.编程的模型结构:
第一步:Java层声明Native方法。
第二步:JNI层实现Java层声明的Native方法,在JNI层可以调用底层库或者毁掉Java层方法。
第三步:加载JNI层代码编译后生成的共享库。
JNI中的JNIEnv是什么:
在C++中:JNIEnv就是struct_JNIEnv。JNIEnv* env等价于struct _JNIEvn* env,在调用JNI函数的时候,只需要env->FindClass(JNIEnv*,const char*),就会间接调用JNINativeInterface结构体里定义的函数指针,而无需先对env解引用。
在C中:JNIEnv就是const struct JNINativeInterface*.JNIEnv* env实际等价于const struct JNINativeInterface** env,因此要先解引用得到*env,才能调用到JNINativeInterface结构体里面定义的函数指针。解引用调用方法即(*env)->FindClass(JNIEnv*,const char*)。
注意:JNIEnv只在当前现成合纵有笑。本地方法不能将JNIEnv从一个现成传递到另一个现成中。相同的java现成中对本地方法多次调用的时候,传递给该本地方法的JNIEnv是相同的。但是一个本地方法可以被不同的Java现成所调用,因此可以接受不同的JNIEnv。
第一节 在Java中调用JNI实现方法
Java数据与JNI数据类型转换。
(Java层调用JNI方法传递参数是Java类型需要通过dalvik虚拟机转换后才能被JNI识别。)
Java类型 | JNI类型 | 字 长 |
boolean | jboolean | 8位 |
byte | jbyte | 8位 |
char | jchar | 16位 |
short | jshort | 16位 |
int | jint | 32位 |
long | jlong | 64位 |
float | jfloat | 32位 |
double | jdouble | 64位 |
void | void | |
JNI方法命名规则:
Java_前缀
全限定的类名
下划线(_)分隔符
增加第一参数JNIEnv* env
增加第二个参数jobject
其他参数按类型映射
返回值按类型映射
例如:
public static native boolean isLoggable(String tag, int level);
对应的JNI方法实现:
static jboolean android_util_Log_isLoggable(JNIEnv* env,jobject clazz,jstring tag,jint level){//...实现代码};
在框架层中可以采用函数注册的方式建立Java层生命方法与JNI层实现方法之间的对应关系,可以不遵守这些规则。
JNI方法签名规则:(用一个字符来识别重载方法)
Java类型 | 类型签名 | Java类型 | 类型签名 |
---|---|---|---|
boolean | Z | long | J |
byte | B | float | F |
char | C | double | D |
short | S | 类 | L全限定类名; |
int | I | 数组 | [元素类型签名 |
long fun(int n , String str,int[] arr);
对应的方法签名:
(ILjava/lang/String;[I)J
JNI操作Java对象(通过操作jobject带来操作Java对象提供的变量和方法):
FindClass和GetObjectClass
在C++中:
jclass FindClass(const char* name);//查找类信息
jclass GeObjectClass(jobject obj);//返回对象的类
在C中:
jclass (*FindClass)(JNIEnv* ,const char*);
jclass (*GetObjectClass)(JNIEnv* , jobject);
注意:通过FindClass传入要查找的类的全限定名(以“/”分隔路径)即可,之后方法返回一个jclass对象,这样就可以操作这个类的方法和变量了。
操作成员变量(域)和方法:
Java中习惯将变量成为成员变量,而不是域。为了兼容JNI命名规则和Java习惯,将域和变量等价。
操作变量=》以Log系统为例:
第一步:通过FindClass方法找到类的信息clazz;
第二步:以clazz为参数调用GetStaticFieldID(clazz,"DEBUG","I");其中DEBUG是要访问的Java域的名字,I是该域的类型签名,即整型。
第三步:GetStaticFieldID函数返回一个jfieldID,代表Java成员变量。最后将该ID传给GetStaticField方法,得到Java层的成员变量DEBUG的值。
操作方法:
与操作成员变量类似,流程如下:
FindClass->GetMethodID(返回jmethodID)->Call<Type>Method
其中Type为方法的修饰符,如public ,private 等等