2,阿里+头条+腾讯等大厂Android面试题分享

在JNI技术中,用来记录这种一一对应关系的,是一个叫JNINativeMethod的结构,其定义如下:

typedef strut{
//Java中native函数的名字,不用携带包的路径,例如“native_init”
const char* name;
//Java函数的签名信息,用字符串表示,是参数类型和返回值类型的组合。
const char* signature;
void* fnPtr;//JNI层对应函数的函数指针,注意它是void* 类型。
}JNINativeMethod;

应该如何使用这个结构呢?来看看MediaScanner JNI层是如何做的

//定义一个JNINativeMethod数组,其成员就是MediaScanner(MS)中所有native函数的一一对应关系。
static JNINativeMethod gMethods[] = {

{
“native_init”,
“()V”,
(void *)android_media_MediaScanner_native_init
},

//注册JNINativeMethod数组
int register_android_media_MediaScanner(JNIEnv *env){
//调用AndoridRuntime的registerNativeMethods函数,第二个参数表明是Java中哪个类
return AndoridRuntime::registerNativeMethods(env,“android/media/MediaScanner”,gMethods,NELEM(gMethods));
}
}

AndroidRunTime类提供了一个registerNativeMethods函数来完成注册工作,下面来看registerNativeMethods的实现

initAndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* classNmae,const JNINativeMethod* gMethods,int numMethods){
//调用jniRegisterNativeMethods函数完成注册
return jniRegisterNativeMethods(env,className,gMeghods,numMethods);
}

其中jniRegisterNativeMethods是Android平台中为了方便JNI使用而提供的一个帮助函数,其代码如下所示:

int jniRegisterNativeMethods(JNIEnv* env,const char* classNmae,
const JNINativeMethod* gMethods,int numMethods){
jclass classzz;
clazz = (*env)->FindClass(env,className);2,阿里+头条+腾讯等大厂Android面试题分享


//实际上调用JNIEnv的RegisterNatives函数完成注册的
if((*env)->RegisterNatives(env,clazz,gMethods,numMethods)<0){
return -1;
}
return 0;
}

其实动态注册的工作,只用两个函数就能完成。

/**

  • env指向一个JNIEnv结构体,它非常重要,后面会讨论它。classname为对应的Java类名,由于JNINativeMethod中使用的函数名并非全路径名,所以要指明是哪个类。
    */
    jclass clazz = (*env)->FindClass(env,classNmae);
    //调用JNIEnv的RegisterNatives函数,注册关联关系。
    (*env)->RegisterNatives(env,clazz,gMethods,numMethods);

所以在自己的JNI层代码中使用这种弄方法,就可以完成动态注册了。这些动态注册的函数在什么时候和什么地方被调用呢?

当Java层通过System.loadLibrary加载完JNI动态库后,紧接着会查找该库中一个叫JNI_Onload的函数。如果有,就调用它,而动态注册的工作就是在这里完成的。

所以,如果想使用动态注册方法,就必须实现JNI_Onload函数,只有这个函数中才有机会完成动态注册的工作。静态注册的方法则没有这个方法,但建议大家也实现这个JNI_OnLoad函数,因为有一些初始化工作是可以在这里做的。

那么,libmedia_jni.so的JNI_OnLoad函数是在哪里实现的额呢?由于多媒体系统很多地方都使用了JNI,所以把它放到了android_media_MediaPlayer.cpp中了,代码如下

jint JNI_OnLoad(JavaVM* vm, void* /* reserved /)
{
//该函数的第一个参数类型为JavaVM,这可是虚拟机在JNI层的代表,每个Java进程只有一个JavaVM
JNIEnv
env = NULL;
jint result = -1;

if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE(“ERROR: GetEnv failed\n”);
goto bail;
}
assert(env != NULL);

if (register_android_media_ImageWriter(env) != JNI_OK) {
ALOGE(“ERROR: ImageWriter native registration failed”);
goto bail;
}
//动态注册MediaScanner的JNI函数
if (register_android_media_ImageReader(env) < 0) {
ALOGE(“ERROR: ImageReader native registration failed”);
goto bail;
}

/* success – return valid version number */
result = JNI_VERSION_1_4;
bail:
return result;
}

JNI层代码中一般要包含jni.h这个头文件。Andorid源码中提供了一个帮助头文件JNIHelp.h,它内部其实就包含了jni.h,所有我们在自己的代码中直接包含这个JNIHelp.h即可

数据类型转换

JNI层代码中一般要包含jni.h这个头文件。Andorid源码中提供了一个帮助头文件JNIHelp.h,它内部其实就包含了jni.h,所有我们在自己的代码中直接包含这个JNIHelp.h即可

数据类型转换

上一篇:C语言自学之路—第三天


下一篇:8.java流程控制