JavaVM与JNIEnv
JNI中其他类型:成员域ID和方法ID
//成员域ID、成员方法ID:
struct _jfieldID;
typedef struct _jfieldID* jfield;
struct _jmethodID;
typedef _jmethodID* jemthodID
jvalue : 可以代表不同类型的变量,注意jboolean和jlong
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
JNI_Onload
JNI_Onload是有共享库导出的,有用户来实现。
执行时机:在一个so被加载时调用
/* Prototypes for functions exported by loadable shared libs. These are called by JNI, not provided by JNI. */
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);//第二个参数是保留的
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);
JavaVM和JNIEnv
struct _JNIEnv;
struct _JavaVM;
typedef const struct JNINativeInterface* C_JNIEnv;
#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif
jnienv在C和cpp中的使用:使用C很繁琐,用CPP比较简洁
// C中
jclass TestJclass = (*env)->FindClass(env,"com/showme/reflectiontest/Test");
jfieldID publicStaticField_jfieldID = (*env)->GetStaticFieldID(env,TestJclass, "publicStaticField","Ljava/lang/String;");
jstring publicStaticField_content = (jstring) (*env)->GetStaticObjectField(env,TestJclass,publicStaticField_jfieldID);
const char *content_ptr = (*env)->GetStringUTFChars(env,publicStaticField_content, NULL);
__android_log_print(4, "showme->jni", "jni->%s", content_ptr);
// cpp中
jclass TestJclass = env->FindClass("com/showme/reflectiontest/Test");
jfieldID publicStaticField_jfieldID = env->GetStaticFieldID(TestJclass, "publicStaticField","Ljava/lang/String;");
jstring publicStaticField_content = (jstring) env->GetStaticObjectField(TestJclass,publicStaticField_jfieldID);
const char *content_ptr = env->GetStringUTFChars(publicStaticField_content, nullptr);
__android_log_print(4, "showme->jni", "jni->%s", content_ptr);
JavaVM
JavaVM是虚拟机在JNI层的代表,一个进程只有一个JavaVM,所有的线程共用一个JavaVM
- JNIInvokeInterface_结构封装和JVM相关的功能函数,如销毁JVM,获得当前线程Java执行环境
- 在C和C++中JavaVM的定义有所不同,在C中JavaVM是JNIInvokeInterface_类型指针,而在C++中又对JNIInvokeInterface_进行了一次封装
- 推荐用C++来编写JNI代码,比C中少了一个参数
JNIEnv
JNIEnv表示Java调用native语言的环境,是一个封装了大量JNI方法的指针
JNIENv只在创建它的线程中生效,不能跨线程传递,不同线程的JNIENv彼此独立
native环境中创建的线程,如果需要访问JNI,必须要调用AttachCurrentThread关联,并使用DetachCurrentThread解除链接
JavaVM的获取
1、在JNI_Onload中作为参数获得,如下:
JNIEXPORT jint JNICALL JNI_Onload(JavaVM* vm,void* reserved),该函数由ART负责自动化查找和传入参数进行调用
2、通过JNIEnv的GetJavaVM函数获取,如下:
JavaVM* thisvjm = nullptr;
env->GetJavaVM(&thisjvm);
native创建的新线程,需要在线程中使用线程自身的JNIEnv
// c创建新线程
pthread_t thread;
pthread_create(&thread, nullptr, threadtest, nullptr);
pthread_join(thread, nullptr);
// 新线程,需要获取自身的JNIEnv,并附加到JavaVM(这个JNIEnv需要知道自身从属于哪个虚拟机)
void *threadtest(void *args) {
JNIEnv *threadenv = nullptr;
if (globalVM->GetEnv((void **) &threadenv, JNI_VERSION_1_6) == JNI_OK);
int result = globalVM->AttachCurrentThread(&threadenv, nullptr);//重要
if (result == JNI_OK);
threadenv->ExceptionDescribe();//打印异常
threadenv->ExceptionClear();//清除异常
globalVM->DetachCurrentThread();//重要
pthread_exit(0);
}
注意事项-这个真的很重要
- JNIEnv是与一个ClassLoader绑定的,当使用env->FindClass()进行类的查询和加载时便是使用的这个ClassLoader(在主线程中可以)
- JNIEnv是当前Java线程的执行环境,一个JVM对应一个JavaVM结构,而一个JVM中可能创建多个Java线程,使用pthread_create新建的线程当使用AttachCurrentThread(&env,NULL)获取到JNIEnv后,该JNIEnv的ClassLoader并不是主线程的ClassLoader,因此也就无法加载app自己的Class (解决方式是:1、主线程在启动子线程时,将ClassLoader当做参数传递,2、在主线程中创建全局的ClassLoader)