手动创建jni工程
可以参考 https://www.jianshu.com/p/2ff3f94328f6
加载Native 库
|
|
JNI_OnLoad
调用System.loadLibrary()函数时, 内部就会去查找so中的 JNI_OnLoad 函数,如果存在此函数则调用。JNI_OnLoad会:告诉 VM 此 native 组件使用的 JNI 版本。对应了Java版本,android中只支持JNI_VERSION_1_2 、JNI_VERSION_1_4、JNI_VERSION_1_6
|
|
JNI_OnUnload
虚拟机释放该C库时,调用JNI_OnUnload()函数
|
|
jni数据类型
Java类型 | 本地类型 | 描述 |
---|---|---|
boolean | jboolean | C/C++8位整型 |
byte | jbyte | C/C++带符号的8位整型 |
char | jchar | C/C++无符号的16位整型 |
short | jshort | C/C++带符号的16位整型 |
int | jint | C/C++带符号的32位整型 |
long | jlong | C/C++带符号的64位整型 |
float | jfloat | C/C++32位浮点型 |
double | jdouble | C/C++64位浮点型 |
Object | jobject | 任何Java对象,或者没有对应java类型的对象 |
Class | jclass | Class对象 |
String | jstring | 字符串对象 |
Object[] | jobjectArray | 任何对象的数组 |
boolean[] | jbooleanArray | 布尔型数组 |
byte[] | jbyteArray | 比特型数组 |
char[] | jcharArray | 字符型数组 |
short[] | jshortArray | 短整型数组 |
int[] | jintArray | 整型数组 |
long[] | jlongArray | 长整型数组 |
float[] | jfloatArray | 浮点型数组 |
double[] | jdoubleArray | 双浮点型数组 |
静态注册
参数说明:
JNIEXPORT :声明jni标志(固定)
jint: 方法返回值
JNICALL :表示要jni要调用方法的标志(固定)
Java_com_hj_jni_MainActivitytest :(具体方法名 格式: Java 包名 类名 方法名)
JNIEnv: 由Jvm传入与线程相关的变量。定义了JNI系统操作、java交互等方法。(固定)
jobject: 表示当前调用对象,即 this , 如果是静态的native方法,则获得jclass (固定)
jintArray b_, jobjectArray:方法具体传的参数
类Unix系统中JNICALL JNIEXPORT 可以省略不加
extern “C”的主要作用就是为了能够正确实现C++代码调用其他C语言代码
|
|
动态注册
|
|
获取参数
基本数据类型
jboolean jbyte jchar jshort jint jlong jfloat jdouble 这些直接拿来用即可
|
|
##String获取
字符串对比 strcmp(const char __lhs, const char __rhs) 字符串对比 若str1=str2,则返回零;若str1
GetStringUTFChars 第二个参数isCopy 表示是否原始字符串java.lang.String的一份拷贝, 在Java规定字符串不可变的规则 所以返回肯定是true 一般传NULL即可。
|
|
获取基本数据类型数组
0
原始数据: 对象数组将不会被限制.
拷贝数据: 数据将会拷贝回原始数据, 同时释放拷贝数据.
JNI_COMMIT
原始数据: 什么都不作.
拷贝数据: 数据将会拷贝回原始数据, 不释放拷贝数据.
JNI_ABORT
原始数据: 对象数组将不会被限制, 之前的数据操作有效..
拷贝数据: 释放拷贝数据, 之前的任何数据操作会丢弃.
|
|
获取其它类型数组
|
|
C/CPP反射Java对象
方法 | 描述 |
---|---|
jclass FindClass(JNIEnv env, const char name) | 返回 Java 类 |
jobject NewObject(JNIEnv *env, jclass clazz, jmethodID methodID, …); | 构造新 Java 对象。方法 ID指示应调用的构造函数方法。该 ID 必须通过调用 GetMethodID() 获得,且调用时的方法名必须为“ < init >” |
jclass GetObjectClass(JNIEnv *env, jobject obj) | 返回 Java 类 |
jfieldID GetFieldID(JNIEnv env, jclass clazz, const char name, const char *sig) | 返回 Java 类对象属性的ID name 属性名 sig 属性签名 |
jmethodID GetMethodID(JNIEnv env, jclass clazz, const char name, const char *sig) | 返回 Java 类对象方法的ID name 方法名 sig 方法签名 |
jfieldID GetStaticFieldID(JNIEnv env, jclass clazz, const char name, const char *sig) | 返回 Java 类对象静态属性的ID name 属性名 sig 属性签名 |
jmethodID GetStaticMethodID(JNIEnv env, jclass clazz, const char name, const char *sig) | 返回 Java 类对象静态方法的ID name 方法名 sig 方法签名 |
获取java传过来的对象信息
|
|
创建java对象
|
|
引用
方法 | 描述 |
---|---|
jobject NewGlobalRef(JNIEnv *env, jobject obj); | 创建全局引用 obj:全局或局部引用 系统内存不足则返回 NULL |
void DeleteLocalRef(JNIEnv *env, jobject localRef) | 删除 localRef 所指向的局部引用 localRef:局部引用 |
void DeleteGlobalRef(JNIEnv *env, jobject globalRef) | 删除 globalRef 所指向的全局引用globalRef:全局引用 |
局部引用
大多数JNI函数会创建局部引用。NewObject/FindClass/NewStringUTF 等等都是局部引用。
局部引用只有在创建它的本地方法返回前有效,本地方法返回后,局部引用会被自动释放。
因此无法跨线程、跨方法使用。
释放一个局部引用有两种方式:
1、本地方法执行完毕后VM自动释放;
2、通过DeleteLocalRef手动释放;
VM会自动释放局部引用,为什么还需要手动释放呢?
因为局部引用会阻止它所引用的对象被GC回收。
|
|
全局引用
全局引用可以跨方法、跨线程使用,直到它被手动释放才会失效 。
由 NewGlobalRef 函数创建
|
|
弱引用
与全局引用类似,弱引用可以跨方法、线程使用。与全局引用不同的是,弱引用不会阻止GC回收它所指向的VM内部的对象 。
在对Class进行弱引用是非常合适(FindClass),因为Class一般直到程序进程结束才会卸载。
在使用弱引用时,必须先检查缓存过的弱引用是指向活动的对象,还是指向一个已经被GC的对象
|
|
线程
JNI 接口指针 (JNIEnv) 仅在当前线程中有效。如果另一个线程需要访问 Java 虚拟机,则该线程首先必须调用 AttachCurrentThread() 以将自身连接到虚拟机并且获得 JNI 接口指针。连接到虚拟机之后,本地线程的工作方式就与在本地方法内运行的普通 Java 线程一样了。本地线程保持与虚拟机的连接,直到调用 DetachCurrentThread() 时才断开连接。
方法 | 说明 |
---|---|
AttachCurrentThread(JNIEnv* p _ env, void thr_args) | 将当前线程连接到 Java 虚拟机 成功时返回“0”;失败则返回负数。 |
DetachCurrentThread(JavaVM *vm) | 断开当前线程与 Java 虚拟机之间的连接 |
|
|