在知道了如何创建一个包含有jni的Android的工程后,现在就需要进一步的了解其原理,不仅要知其然,还要知其所以然。话虽这么说,其实我对这块内容也是刚刚入门,因此这个仅仅作为我的一个笔记,如果其中内容有误,还希望大家指正。
之前刚刚接触jni的时候,在网络上看了不少东西,基本是网上的各种demo代码都有两种写法,比如说jni回调常用到的函数FindClass,网上肯定能找到两种调用方法:
(*env)->FindClass(env, "xxx/xxxx/xxxx");
env->FindClass("xxx/xxxx/xxxx");
有些文章由于直接贴上了代码,但是在编译时可能就会出错。为什么会这样??写过C/C++代码童鞋可能一眼就能看出其中的原因,但是没有相关经验的人可能就会有些迷糊了。
其实所有的原因都在jni.h中,这里以\android-ndk-r8b\platforms\android-14\arch-arm\usr\include\jni.h来深入。
我们自己实现jni代码中都会包含jni.h,该头文件中定义所有和JNI相关的类型和接口。
首先基本数据类型定义
# include <inttypes.h> /* C99 */ typedef uint8_t jboolean; /* unsigned 8 bits */ typedef int8_t jbyte; /* signed 8 bits */ typedef uint16_t jchar; /* unsigned 16 bits */ typedef int16_t jshort; /* signed 16 bits */ typedef int32_t jint; /* signed 32 bits */ typedef int64_t jlong; /* signed 64 bits */ typedef float jfloat; /* 32-bit IEEE 754 */ typedef double jdouble; /* 64-bit IEEE 754 */ #else typedef unsigned char jboolean; /* unsigned 8 bits */ typedef signed char jbyte; /* signed 8 bits */ typedef unsigned short jchar; /* unsigned 16 bits */ typedef short jshort; /* signed 16 bits */ typedef int jint; /* signed 32 bits */ typedef long long jlong; /* signed 64 bits */ typedef float jfloat; /* 32-bit IEEE 754 */ typedef double jdouble; /* 64-bit IEEE 754 */ #endif /* "cardinal indices and sizes" */ typedef jint jsize;
知道这个的话,在读jni相关代码的时,就可以无视那个”j“了。
#ifdef __cplusplus /* * Reference types, in C++ */ class _jobject {}; class _jclass : public _jobject {}; class _jstring : public _jobject {}; class _jarray : public _jobject {}; class _jobjectArray : public _jarray {}; class _jbooleanArray : public _jarray {}; class _jbyteArray : public _jarray {}; class _jcharArray : public _jarray {}; class _jshortArray : public _jarray {}; class _jintArray : public _jarray {}; class _jlongArray : public _jarray {}; class _jfloatArray : public _jarray {}; class _jdoubleArray : public _jarray {}; class _jthrowable : public _jobject {}; typedef _jobject* jobject; typedef _jclass* jclass; typedef _jstring* jstring; typedef _jarray* jarray; typedef _jobjectArray* jobjectArray; typedef _jbooleanArray* jbooleanArray; typedef _jbyteArray* jbyteArray; typedef _jcharArray* jcharArray; typedef _jshortArray* jshortArray; typedef _jintArray* jintArray; typedef _jlongArray* jlongArray; typedef _jfloatArray* jfloatArray; typedef _jdoubleArray* jdoubleArray; typedef _jthrowable* jthrowable; typedef _jobject* jweak; #else /* not __cplusplus */ /* * Reference types, in C. */ typedef void* jobject; typedef jobject jclass; typedef jobject jstring; typedef jobject jarray; typedef jarray jobjectArray; typedef jarray jbooleanArray; typedef jarray jbyteArray; typedef jarray jcharArray; typedef jarray jshortArray; typedef jarray jintArray; typedef jarray jlongArray; typedef jarray jfloatArray; typedef jarray jdoubleArray; typedef jobject jthrowable; typedef jobject jweak; #endif /* not __cplusplus */
这里是一些常见的数据结构的定义,可以看出这里预处理命令”#ifdef __cplusplus“,如果jni是C++实现的话,相关变量其实是一些class的指针;在C里面则更加简单,全部都是”void*“的typedef。
再下面是其他的数据结构的定义,在jni编程中最重要也是最常见的类型 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
struct JNINativeInterface { void* reserved0; void* reserved1; void* reserved2; void* reserved3; jint (*GetVersion)(JNIEnv *); jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, jsize); jclass (*FindClass)(JNIEnv*, const char*);// 省略}
struct _JNIEnv { /* do not rename this; it does not seem to be entirely opaque */ const struct JNINativeInterface* functions; #if defined(__cplusplus) jint GetVersion() { return functions->GetVersion(this); } jclass DefineClass(const char *name, jobject loader, const jbyte* buf, jsize bufLen) { return functions->DefineClass(this, name, loader, buf, bufLen); } jclass FindClass(const char* name) { return functions->FindClass(this, name); } // 省略多个函数... }
从上面三个代码段可以看出无论是C或者C++中的定义的核心都是JNINativeInterface,该结构体中保存了4个值,具体内容是什么,我现在也不清楚,但可以肯定和当前调用的线程有关系。JNINativeInterface中其他都是函数的指针。
可以看出在C中JNIEnv其实就是JNINativeInterface*,于是函数的参数JNIEnv *env 其实就是JNINativeInterface**,代码(*env)->FindClass(env, "xxx/xxxx/xxxx")就不难理解了:*env得到JNINativeInterface结构体的地址,然后->FindClass找到具体函数的地址,然后执行相关代码。.
在C++中JNIEnv则是_JNIEnv结构,其实就是一个类,该类有一个成员变量JNINativeInterface* functions;其他的方法仔细看的话其实就是对JNINativeInterface结构中相关方法的一个简单封装而已。于是我们知道JNIEnv *env在C++中表示类_JNIEnv的一个指针,env->FindClass("xxx/xxxx/xxxx");则是类对方法调用的方式。
同理JavaVM在jni.h中定义和JNIEnv 差不多。
JNI中比较重要的两个函数也是在jni.h中声明的:
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved); JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);
尤其是JNI_OnLoad函数,如果没有实现该函数,那么在load相关so库时会有提示JNI_Onload not found。
嗯,感觉写了半天也没什么重点,算了,目前先写这么多,日后有进展再更新。