Android基础--JNI

1 JNI_OnLoad

Dalvik虚拟机加载C库时,即执行System.loadLibrary()函数时,第一件事是调用JNI_OnLoad()函数。可以在JNI_OnLoad 去注册方法

JNI_OnLoad --> registerNativeMethods

Android系统加载JNI Lib的方式

  1. 通过JNI_OnLoad
  2. 如果JNI Lib没有定义JNI_OnLoad,则dvm调用dvmResolveNativeMethod进行动态解析

2 JavaVM

JavaVM 是 Java虚拟机在 JNI 层的代表, JNI 全局只有一个, 即一个进程只有一个。

3 JNIEnv

顾名思义,指代了Java本地接口环境(Java Native Interface Environment),是一个JNI接口指针,指向了本地方法的一个函数表,该函数表中的每一个成员指向了一个JNI函数,本地方法通过JNI函数来访问JVM中的数据结构

3.1 JNIEnv 作用

调用 Java 函数 : JNIEnv 代表 Java 运行环境, 可以使用 JNIEnv 调用 Java 中的代码;
操作 Java 对象 : Java 对象传入 JNI 层就是 Jobject 对象, 需要使用 JNIEnv 来操作这个 Java 对象

3.2 JNIEnv 与线程

JNIEnv指针只在它所在的线程中有效,不能跨线程传递和使用。不同线程调用一个本地方法时,传入的JNIEnv指针是不同的.
比如,在jni的方法中起了线程去处理事件,处理完后希望能通知java层,线程中是不能使用参数JNIEnv的。
解决的方法可以通过JavaVM 的AttachCurrentThread方法去获取当前线程的JNIEnv

A JNI interface pointer (JNIEnv*) is passed as an argument for each native function mapped to a Java method, allowing for interaction with the JNI environment within the native method.This JNI interface pointer can be stored, but remains valid only in the current thread. Other threads must first call AttachCurrentThread()to attach themselves to the VM and obtain a JNI interface pointer. Once attached, a native thread works like a regular Java thread running within a native method. The native thread remains attached to the VM until it callsDetachCurrentThread() to detach itself.[3

如果在当前线程调用了DetachCurrentThread() 程序会报错, JDK1.2以后提供了一个新调用接口(invocation interface)函数GetEnv,这样,你就可以检查当前线程是否被附加到JVM上,然后返回属于当前线程的JNIEnv指针。如果当前线程已经被附加到VM上的话,GetEnv和AttachCurrentThread在功能上是等价的。

    int status = javaVM->GetEnv((void**)&env, JNI_VERSION_1_4);
    if (status < 0) {
        javaVM->AttachCurrentThread(&env, NULL);
        ALOGE("AttachCurrentThread");
    }
    ...
    if (status < 0) {
        javaVM->DetachCurrentThread();
    }

4 jobject与jclass类型

object与jclass通常作为JNI函数的第二个参数,当所声明Native方法是静态方法时,对应参数jclass,因为静态方法不依赖对象实例,而依赖于类,所以参数中传递的是一个jclass类型。相反,如果声明的Native方法时非静态方法时,那么对应参数是jobject。

jobject 也是不能跨线程调用的。解决的方法是通过全局引用来获取
env->NewGlobalRef(obj)

JavaVM *g_jvm = NULL;
jobject g_obj = NULL;
//由java调用来建立JNI环境
JNIEXPORT void Java_****_setJNIEnv( JNIEnv* env, jobject obj)
 {
     //保存全局JVM以便在子线程中使用
     env->GetJavaVM(&g_jvm);
     //不能直接赋值(g_obj = obj)
     g_obj = env->NewGlobalRef(obj);
 }
//线程里面
     JNIEnv *env;
     jclass cls;
     jmethodID mid;
 
     //Attach主线程
     if(g_jvm->AttachCurrentThread(&env, NULL)  !=  JNI_OK)
     {
         return NULL;
     }
     //找到对应的类
     cls = env->GetObjectClass(g_obj);
     if(cls == NULL)
     {
         goto error;
     }
     //再获得类中的方法
     mid = env->GetMethodID(cls, "fromJNI", "(I)V");
     if (mid == NULL)
     {
         goto error; 
     }
     //最后调用java中的静态方法
     env->CallVoidMethod(cls, mid ,(int)arg);
 
error:
     return NULL;

不再使用时记得调用相关函数去释放

g_vm->DetachCurrentThread();
env->DeleteLocalRef(g_obj);

参考:
https://blog.csdn.net/fireroll/article/details/50102009
https://blog.csdn.net/CV_Jason/article/details/80026265
https://blog.csdn.net/u011068702/article/details/78066746

你可能感兴趣的:(Android基础--JNI)