在C的实现方法里面, 创建一个Java对象的几个步骤:
- 第一,findClass找到需要创建对象的类(全类名)
- 第二,得到构造方法的ID,构造方法名称,统一使用<init>
- 第三,使用NewObject创建Java对象
当创建了这个类的对象之后 , 我们就可以使用这个类里面所提供的方法了 , 那么我们就可以在C中使用Java中其他对象的方法了
数组的引用处理(主要是同步问题)
Java方法中,通过调用accessField,利用C修改静态属性
//数组处理
public native void sortArray(int array[]);
public static void main(String[] args) {
JniTest test = new JniTest();
int arr[] = { 3, 2, 4, 5, 1, 0 };
//调用C的函数进行快速排序
test.sortArray(arr);
System.out.println(Arrays.toString(arr));
}
C代码如下:
int compare(const int * a, const int * b){
return (*a) - (*b);
}
JNIEXPORT void JNICALL Java_com_test_JniTest_sortArray
(JNIEnv * env, jobject jobj, jintArray arr){
//创建Java数组
//(*env)->NewIntArray(env, len);
//通过Java的数组,拿到C的数组的指针
jint* c_arr = (*env)->GetIntArrayElements(env, arr, NULL);
//获取Java数组的大小
jsize len = (*env)->GetArrayLength(env, arr);
//排序,其中compare是函数指针,用于比较大小,与Java类似
qsort(c_arr, len, sizeof(jint), compare);
//操作完之后需要同步C的数组到Java数组中
(*env)->ReleaseIntArrayElements(env, arr, c_arr, 0);
}```
__注意:__
1、通过GetIntArrayElements拿到C类型的数组的指针,然后才能进行C数组的处理。
2、C拿到Java的数组进行操作或者修改以后,需要调用ReleaseIntArrayElements进行更新,这时候Java的数组也会同步更新过来。
这个方法的最后一个参数是模式:
0: Java数组进行更新,并且释放C/C++数组。
JNI_ABORT: Java数组不进行更新,但是释放C/C++数组。
JNI_COMMIT: Java数组进行更新,不释放C/C++数组(函数执行完,数组还是会释放)。```
引用的分级
在Java中引用也有强弱之分 , 使用new创建的对象就是强引用,也可以使用WeakReference将对象包装成一个弱引用对象 。在C中也不列外 , C中也有一套全局引用,局部引用,弱全局引用等等
一 , 局部引用
// 局部引用
// 作用:C使用到或自行创建Java对象,需要告知虚拟机在合适的时候回收对象
//局部引用,通过DeleteLocalRef手动释放对象
//1.访问一个很大的java对象,使用完之后,还要进行复杂的耗时操作
//2.创建了大量的局部引用,占用了太多的内存,而且这些局部引用跟后面的操作没有关联性
JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJNI_localRef
(JNIEnv *env, jobject jobj) {
// 找到类
jclass dateClass = (*env)->FindClass(env, "java/util/Date");
// 得到构造方法ID
jmethodID dateConstructorId = (*env)->GetMethodID(env, dateClass, "<init>", "()V");
// 创建Date对象
jobject dateObject = (*env)->NewObject(env, dateClass, dateConstructorId);
// 创建一个局部引用
jobject dateLocalRef = (*env)->NewLocalRef(env, dateObject);
// 省略N行代码
// 不再使用对象 , 则通知GC回收对象
(*env)->DeleteLocalRef(env, dateLocalRef);
// 因为dateObject也是局部对象,可以直接回收dateObject对象
//(*env)->DeleteLocalRef(env, dateObject);
}```
__全局引用__
// 全局引用
// 定义全局引用
//共享(可以跨多个线程),手动控制内存使用
jstring globalStr;
/创建全局引用/
JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJNI_createGlobalRef
(JNIEnv *env, jobject jobj) {
jstring jStr = (*env)->NewStringUTF(env, "I want your love !");
// 创建一个全局引用
globalStr = (*env)->NewGlobalRef(env, jStr);
}
/使用全局引用/
JNIEXPORT jstring JNICALL Java_com_zeno_jni_HelloJNI_useGlobalRef
(JNIEnv *env, jobject jobj) {
return globalStr;
}
/释放全局引用/
JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJNI_deleteGlobalRef
(JNIEnv *env, jobject jobj) {
// 释放全局引用
(*env)->DeleteGlobalRef(env, globalStr);
}
//弱全局引用
//节省内存,在内存不足时可以是释放所引用的对象
//可以引用一个不常用的对象,如果为NULL,临时创建
//创建:NewWeakGlobalRef,销毁:DeleteGlobalWeakRef
__引用缓存__
/变量缓存/
JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJNI_variableCach
(JNIEnv *env, jobject jobj) {
// 找到String类
jclass stringClass = (*env)->FindClass(env, "java/lang/String");
// 得到构造方法ID
jmethodID stringConstructorID = (*env)->GetMethodID(env, stringClass, "<init>", "()V");
// 创建String类
//// 缓存局部变量 , 只创建一次 , 关键字static
static jobject stringObject = NULL;
if (stringObject == NULL)
{
stringObject = (*env)->NewObject(env, stringClass, stringConstructorID);
printf("------------- create String object --------------\n");
}
/*jobject stringObject = (*env)->NewObject(env, stringClass, stringConstructorID);
printf("------------- create String object --------------\n");*/
}```
引用的分级 ,上述代码都有比较详细的注释 ,这里就不多加解释了 , 说一下全局引用的简单使用场景。
在开发中 , 我们常常需要初始化一些变量 , 进行全局使用 , 这里我们的全局引用就发挥了作用了 。
// 初始化全局变量
//初始化全局变量,动态库加载完成之后,立刻缓存起来
jstring initGlobalStr;
JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJNI_initVariable
(JNIEnv *env, jclass jcls) {
jstring initStr = (*env)->NewStringUTF(env, "create global init variable ");
initGlobalStr = (*env)->NewGlobalRef(env, initStr);
}
/*访问初始化全局变量*/
JNIEXPORT jstring JNICALL Java_com_zeno_jni_HelloJNI_accessInitGlobalVariable
(JNIEnv *env, jobject jobj) {
return initGlobalStr;
}
__java code__
static{
// 加载动态库
System.loadLibrary("Hello_JNI") ;
// 初始化全局变量
initVariable();
}```
C中引用的分级和在Java中的类型 , 都需要在合适的环境使用