JNI原理分析

一基于开发Android的C++必备知识

自我感觉Android开发JNI仅仅是为了调用c库完成一些操作,所以仅仅需要了解基础的c/c++就足够了,首先jni的本地函数的必备参数就是两个结构体指针,所以最先学习结构体指针

#includestruct smile

{

char sna;

char size;

float price;

};

int main(void)

{

struct smile x;

x.sna = 'M';

x.size = 'B';

x.price = 20.5;

printf( "%c, %c, %.1f", x.sna, x.size, x.price );

return 0;}

这是一个典型的结构体定义和调用的写法,和Java的区别是不需要new对象,结构体成员变量的访问除了可以借助符号".",还可以用"->"访问,这就需要结构体的指针,后面我们会知道本地函数会将Java对象的地址传递给c,所以jni中都是指针访问的形式

#include#includestruct smile {

char sna;

float price;

};

int main(void)

{

struct smile x;

struct smile *px;

px = &x;

px->sna = 'R';

px->price = 26.8;

printf( "Sna=[%c], Price=%.1f", x.sna, x.price );

}

上面就是指针访问的形式,将定义的struct对象指针赋值给*px,就可以操作地址,因为java和c的对象不通用,所以,他们互相调用的原理虚拟机就是通过指针进行地址操作,下面就分析jni的调用过程,当我们调用system.loadlibrary()时,vm就会调用JNI_OnLoad()函数,这个函数的源码地址是/system/lib/libmedia_jni

// #define LOG_NDEBUG 0

#define LOG_TAG "MediaPlayer-JNI"

// ………

jint JNI_OnLoad(JavaVM* vm, void* reserved) {

JNIEnv* env = NULL;

jint result = -1;

if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {

LOGE("ERROR: GetEnv failed\n"); goto bail;

}

assert(env != NULL);

if (register_android_media_MediaPlayer(env) < 0) {

LOGE("ERROR: MediaPlayer native registration failed\n");

goto bail;

}

/* success -- return valid version number */

result = JNI_VERSION_1_4;

bail: return result;

}

此函数回传JNI_VERSION_1_4值给VM,于是VM知道了其所使用的JNI版本了。此外, JNI_OnLoad()函数也做了一些初期的动作,例如指令:

if (register_android_media_MediaPlayer(env) < 0) {

LOGE("ERROR: MediaPlayer native registration failed

\n");

goto bail;

}

就将此*.so的<函数名称表>登记到VM里,以便能加快后续调用本地函数之效率,Java类别透过VM而调用到本地函数。一般是仰赖VM去寻找*.so里的本地函数。如果需要连续调用很多次,每次都需要寻找一遍,会多花许多时间。此时,将此*.so的<函数名称表>登记到VM里。

接下来.so中我们实现我们的本地函数:

JNIEXPORT jstring JNICALL Java_test_yuan_codelearntest_Utils_JniUtils_test(JNIEnv *env,jobject obj)

{

returnenv -> NewStringUTF("Hello World!");

}

jobject obj表示我们的本地函数所在的类的对象,JNIEnv *env这个参数可以理解为虚拟机指针,因为虚拟机是链接Java和c的桥梁,我们可以利用这个指针操作java的性能,比如我们可以拿到对象的class:jclass clazz = (*env)->GetObjectClass(env, obj);这样就可以做一些反射的操作,比如拿到函数id:  m_mid = (*env)->GetMethodID(env, clazz, "setV", "(I)V");从C调用java函数:(*env)->CallVoidMethod(env, thiz, m_mid, sum);等等操作

你可能感兴趣的:(JNI原理分析)