一、java 层调用jni 层的方法,注册之后可以直接使用
1.前几天学了jni 注册的两种方式1)静态注册,头文件注册:通过包名+方法名称特殊的组合将java native 方法映射到 jni 层方法
2)动态注册:1.在system.loadLibrary 之后会回调JNI_OnLoad函数
2.jclass clazz = env->FindClass(env,classname);
env->registerNatives(env,clazz,gMethods,numMethods);
#include <jni.h>
#include <string>
//动态注册
jstring stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
jstring helloFromJNI(JNIEnv *env, jobject instance) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
jint add(JNIEnv *env, jobject instance, jint a, jint b) {
return a + b;
}
static const JNINativeMethod gMethods[] = {
{"stringFromJNI", "()Ljava/lang/String;", (void *) stringFromJNI},
{"helloFromJNI", "()Ljava/lang/String;", (void *) helloFromJNI},
};
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) //从JavaVM获取JNIEnv,一般使用1.4的版本
return -1;
jclass clazz = env->FindClass("demo/com/jnitest/MainActivity");
if (!clazz) {
return -1;
}
if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) {
return -1;
}
return JNI_VERSION_1_4;
}
二、jni 层调用java 层的方法
1)jclass clazz = env->FindClass("classname") 例:env->FindClass("android/media/MediaScannerClinet") 找到jclass 类
2)jmethodId jMethodId = env->GetMethodId(clazz,"方法名",“方法签名”);找到方法id 例:
jmethodId scanfilemethodid = env->GetMethodId(clazz,"scanFile","(Ljava/lang/String;JJ)V")
3)env->Call<Type>Method(jclass,jmethodId,方法参数...);通过类+方法id+方法签名+方法参数 调用jni 方法;例子:
env->CallVoidMethod(clazz ,scanfilemethodid,"(Ljava/lang/String;JJ)V")
4)获取/设置属性值
jfield mfieldId = env->GetFieldId();env->GetIntField();env->setIntField();
Jstr=(*env)->GetObjectField(env,obj,fid); 因为字符串和数组是特殊的对象,所以我们使用GetObjectField 来访问字符串类型的实例字段。
三、jstring 相关函数
1.native->jstring env->NewString(参数unicode 字符串) env->NewStringUTF(参数utf-8字符串)
第二个方法是常用的方法
2.释放资源:调用上面相关方法之后需要调用Release函数释放相关资源
env->ReleaseStringChars();env->ReleaseStringUTFChars()
四、垃圾回收
//保存java 层传入的jobect 对象
save_this = thiz//jni 层这种赋值语句不会增加引用计数
java 层的对象有可能会被回收,引发问题!!
jni 提供三种类型引用解决这一问题
1.Local Reference:本地引用。JNI 层函数中使用的非全局引用都是Local Reference,包括参数传入的jobject 和函数中创建的jobect
特点:一旦函数返回,这些jobject 就可能被垃圾回收
2.Globle Reference :全局引用;这种对象如果不主动释放,它永远不会被回收
3.Weak Global Reference:弱全局引用,一种特殊的 Globle Reference,运行过程中可能被回收!使用之前需要调用、
env->IsSameObject 判断是否被回收了
用的最多的是 Local Reference 和 Globle Reference
jstring pathstring;
DeleteLocalRef(pathstring)
mclient= env->NewGloleRef(clinet)
mEnv->DeleteGlobleRef(mclient)
五、异常处理
JNI 中也有异常不过跟c++、java 中的异常不同。如果JNIEnv 的某些函数出错了,会产生一个异常但是不会中断本地函数的执行,知道从JNI 层返回到java 层之后,虚拟机才会抛出异常。虽然不会中断本地函数的执行,一旦产生异常后,就只能做一些资源清理的工作。如果这时调用除了产生异常函数以外的其他函数则会导致程序死掉!
JNI层提供了三个函数来帮助截获修改这些异常
1.ExceptionOccured -> 判断是否发生异常
2.ExceptionClear -> 用来清理 JNI 层中发生的异常
3.ThrowNew 函数:用来像java 层抛出异常。
if (env->ExceptionOccurred()) {
env->ExceptionDescribe(); // writes to logcat
env->ExceptionClear();
}