Pich Blog

关注移动开发,大数据,云计算,软件架构!

9.MediaScanner的JNI层分析

概述

7.1.1_r6

MS的JNI代码在android_media_MediaScanner.cpp

可以看到他们实现了Java中声明的native函数,但是native和java函数式怎么对应的呢?这就涉及到jni函数注册。

c++
static void
android_media_MediaScanner_native_init(JNIEnv *env)
{
ALOGV("native_init");
jclass clazz = env->FindClass(kClassMediaScanner);
if (clazz == NULL) {
return;
}

<pre><code>fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.context == NULL) {
return;
}
</code></pre>

}

static void
android_media_MediaScanner_processFile(
JNIEnv *env, jobject thiz, jstring path,
jstring mimeType, jobject client)
{
...

<pre><code>env->ReleaseStringUTFChars(path, pathStr);
if (mimeType) {
env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
}
</code></pre>

}

<pre><code><br />## 注册JNI函数

注册就是讲java层的native函数和jni层对应实现的函数关联起来。有了这种关联,就能调用到本地方法了。

### 静态方法

根据函数名来对应JNI函数。

#### 使用流程

编写java代码,然后编译生成class文件

​ javac cn/woblog/jni/TestMain.java

使用javah生成头文件,一般名称为packagename_class.h

​ javah cn.woblog.jni.TestMain

用c/c++实现头文件里面的函数

该实例在[3.JNI开发流程]()中讲解了

总结:

调用java层native函数时,虚拟机会从对应的JNI库中寻找对应的函数,并且建立联系,其实就是保持JNI层函数的函数指针,以后调用native函数时直接调用这个函数指针,当前这些工作是虚拟机完成的。

弊端:

所生成的函数和实现函数要同名

初次调用时会根据函数名搜索JNI层函数建立联系,有效率影响

为了解决这些问题,就产生了动态注册

### 动态注册

因为注册就是native函数和jni函数关联,那么可以手动给他关联上,就不用他扫描了。

#### JNINativeMethod

用来记录native和jni函数对应的结构体。定义在jni.h

c
typedef struct {
//Java中native函数的名称,native_init
const char* name;

//native函数的签名,包括参数返回值
const char* signature;
//JNI层对应的函数指针(任意类型)
void* fnPtr;
} JNINativeMethod;

那该如何实现用呢?看看MS的jni层就知道了。

android_media_MediaScanner.cpp

c++
//定义一个JNINativeMethod数组,每个成员就是所有的native函数
static const JNINativeMethod gMethods[] = {
{
"processDirectory", //Java中native函数的函数名
"(Ljava/lang/String;Landroid/media/MediaScannerClient;)V", //签名
(void *)android_media_MediaScanner_processDirectory //对应的JNI层函数
},

<pre><code>{
"processFile",
"(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
(void *)android_media_MediaScanner_processFile
},

{
"setLocale",
"(Ljava/lang/String;)V",
(void *)android_media_MediaScanner_setLocale
},

{
"extractAlbumArt",
"(Ljava/io/FileDescriptor;)[B",
(void *)android_media_MediaScanner_extractAlbumArt
},

{
"native_init",
"()V",
(void *)android_media_MediaScanner_native_init
},

{
"native_setup",
"()V",
(void *)android_media_MediaScanner_native_setup
},

{
"native_finalize",
"()V",
(void *)android_media_MediaScanner_native_finalize
},
</code></pre>

};

// 注册gMethods里面的方法
// 在android_media_MediaPlayer.cpp的JNI_OnLoad方法中调用了该方法
// /frameworks/base/core/jni/AndroidRuntime.cpp
int register_android_media_MediaScanner(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env,
kClassMediaScanner, gMethods, NELEM(gMethods));
}

<pre><code><br />#### jniRegisterNativeMethods

是为了方便使用JNI提供的一个帮助函数。

JNIHelper.cpp

c++
extern “C” int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);

ALOGV(“Registering %s’s %d native methods…”, className, numMethods);

scoped_local_ref<jclass> c(env, findClass(env, className));
if (c.get() == NULL) {
char* msg;
asprintf(&msg, “Native registration unable to find class ‘%s’; aborting…”, className);
e->FatalError(msg);
}

if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
char* msg;
asprintf(&msg, “RegisterNatives failed for ‘%s’; aborting…”, className);
e->FatalError(msg);
}

return 0;
}

可以看到其实动态注册很简单,只需要两个函数,FindClass,RegisterNatives只是7.0保存这个变量使用了引用。

2.2.3

c++
int jniRegisterNativeMethods(JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;

<pre><code>LOGV("Registering %s natives\n", className);
clazz = (*env)->FindClass(env, className);
if (clazz == NULL) {
LOGE("Native registration unable to find class '%s'\n", className);
return -1;
}
if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
LOGE("RegisterNatives failed for '%s'\n", className);
return -1;
}
return 0;
</code></pre>

}

<pre><code><br />那在什么位置调用注册方法呢,其实是JNI_OnLoad方法

#### JNI_OnLoad

当java调用loadLibrary加载完JNI动态库后,接着调用JNI_OnLoad函数(如果有),就算是静态注册,这里也可以做一些初始化工作

c++

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;

//判断虚拟机版本
//JavaVM,全局只有一个
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;
}

if (register_android_media_MediaRecorder(env) < 0) {
LOGE(“ERROR: MediaRecorder native registration failed\n”);
goto bail;
}

if (register_android_media_MediaScanner(env) < 0) {
LOGE(“ERROR: MediaScanner native registration failed\n”);
goto bail;
}

if (register_android_media_MediaMetadataRetriever(env) < 0) {
LOGE(“ERROR: MediaMetadataRetriever native registration failed\n”);
goto bail;
}

#ifndef NO_OPENCORE
if (register_android_media_AmrInputStream(env) < 0) {
LOGE(“ERROR: AmrInputStream native registration failed\n”);
goto bail;
}
#endif

if (register_android_media_ResampleInputStream(env) < 0) {
LOGE(“ERROR: ResampleInputStream native registration failed\n”);
goto bail;
}

if (register_android_media_MediaProfiles(env) < 0) {
LOGE(“ERROR: MediaProfiles native registration failed”);
goto bail;
}

/* success — return valid version number */
//注册成功,返回该值
result = JNI_VERSION_1_4;

bail:
return result;
}

JNIHelper

JNI层代码中包含一jni.h,但是还提供了一个JNIHelper文件,内部直接包含了JNI.h,他提供了一个写常用的方法。

比如:抛出一个异常,注册JNI方法。

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注