AndroidO 平台JNI机制的学习

第一章 JNI的含义

JNI全称Java Native Interface,意指Java本地调用。JNI是一种技术,通过JNI我们可以做到如下两点:

(1)    Java程序中的函数可以调用到Native语言编写的函数。Java-- >c/c++

(2)    Native程序中的函数也可反向调用Java层的函数。c/c++  -- > Java

在Android平台,JNI是上层Java与Native层沟通的桥梁,示意图如下:

            

         图1 JNI通信结构            图2 Android示例MediaScanner

本文基于《深入理解Android卷一》进行对AndroidO平台进行解析JNI技术的实质过程。

(1)    Java 层MediaScanner是对应于上层Media。

(2)    JNI 层libmedia_jni.so是动态库,模块media命名,是一个桥梁。

(3)    Native层libmedia.so是动态库,是真实的c/c++底层Native的具体实现。

涉及到的文件路径:

android8.1_trunk/frameworks/base/media/java/android/media/MediaPlayer.java

android8.1_trunk/frameworks/base/media/java/android/media/MediaScanner.java

android8.1_trunk/prebuilts/ndk/r11/platforms/android-24/arch-arm/usr/include/jni.h

android8.1_trunk/libnativehelper/JNIHelp.cpp

android8.1_trunk/frameworks/base/core/jni/AndroidRuntime.cpp

android8.1_trunk/frameworks/base/media/jni/android_media_MediaPlayer.cpp

android8.1_trunk/frameworks/base/media/jni/android_media_MediaScanner.cpp

android8.1_trunk/frameworks/av/media/libmedia/MediaScanner.cpp

android8.1_trunk/frameworks/av/media/libstagefright/StagefrightMediaScanner.cpp

本文档基于JNI的实际流程去解析全过程,按顺序:

1.      简要介绍JNI层的数据结构类型

2.      Java层加载JNI库,并书写响应功能的native函数

3.      JNI层注册JNI函数

4.      Native层与Java层通过JNI互相调用的实现机制

第二章 JNI层的数据结构

Java层和JNI层对应的数据类型是不一样的,但是有对应的转换关系。其类型的具体定义是在如下路径文件:

android8.1_trunk/prebuilts/ndk/r11/platforms/android-24/arch-arm/usr/include/jni.h

该头文件定义了所有的JNI层的数据结构类型以及JNI调用Java的方法集。

 


上面两个表说明了Java和JNI数据类型的对应关系,在头文件jni.h中都可以查到,一般不属于基本类型的java类对象,在jni层使用jobject表示。

30/*

31 * Primitive types that match up with Java equivalents.

32 */

33#ifdefHAVE_INTTYPES_H

34    #include <inttypes.h>      /* C99 */

35   typedef uint8_t         jboolean;       /* unsigned 8 bits */

36   typedef int8_t          jbyte;         /* signed 8 bits */

37   typedef uint16_t        jchar;          /* unsigned 16 bits */

38   typedef int16_t        jshort;        /* signed 16 bits */

39   typedef int32_t         jint;           /* signed 32 bits */

40   typedef int64_t         jlong;         /* signed 64 bits */

41   typedef float           jfloat;        /* 32-bit IEEE 754 */

42   typedef double          jdouble;       /* 64-bit IEEE 754 */

43#else

44   typedef unsigned char   jboolean;      /* unsigned 8 bits */

45   typedef signed char     jbyte;         /* signed 8 bits */

46   typedef unsigned short  jchar;         /* unsigned 16 bits */

47   typedef short           jshort;        /* signed 16 bits */

48   typedef int             jint;          /* signed 32 bits */

49   typedef long long       jlong;         /* signed 64 bits */

50   typedef float           jfloat;        /* 32-bit IEEE 754 */

51   typedef double          jdouble;       /* 64-bit IEEE 754 */

52#endif

53

54/* "cardinal indices and sizes" */

55typedefjint            jsize;

57#ifdef__cplusplus          // 如果定义使用c++数据类型,相反是c数据类型

58   /*

59    * Reference types, in C++

60    */

61   class _jobject {};

62   class _jclass :public _jobject {};

63   class _jstring :public _jobject {};

64   class _jarray :public _jobject {};

65   class _jobjectArray : public _jarray {};

66   class _jbooleanArray :public _jarray {};

67   class _jbyteArray :public _jarray {};

68   class _jcharArray :public _jarray {};

69   class _jshortArray :public _jarray {};

70   class _jintArray :public _jarray {};

71   class _jlongArray :public _jarray {};

72   class _jfloatArray :public _jarray {};

73   class _jdoubleArray : public _jarray {};

74   class _jthrowable :public _jobject {};

75

76   typedef _jobject*       jobject;

77   typedef _jclass*        jclass;

78   typedef _jstring*       jstring;

79   typedef _jarray*        jarray;

80   typedef _jobjectArray jobjectArray;

81   typedef _jbooleanArray* jbooleanArray;

82   typedef _jbyteArray*    jbyteArray;

83   typedef _jcharArray*    jcharArray;

84   typedef _jshortArray*   jshortArray;

85   typedef _jintArray*     jintArray;

86   typedef _jlongArray*    jlongArray;

87   typedef _jfloatArray*   jfloatArray;

88   typedef _jdoubleArray jdoubleArray;

89   typedef _jthrowable*    jthrowable;

90   typedef _jobject*       jweak;

91

92

93#else/* not __cplusplus */                // 不使用c++,使用c类型数据

95   /*

96    * Reference types, in C.

97    */

98   typedef void*           jobject;

99   typedef jobject         jclass;

100   typedefjobject         jstring;

101   typedef jobject         jarray;

102   typedef jarray          jobjectArray;

103   typedef jarray          jbooleanArray;

104    typedefjarray          jbyteArray;

105   typedef jarray          jcharArray;

106   typedef jarray          jshortArray;

107   typedef jarray          jintArray;

108   typedef jarray          jlongArray;

109   typedef jarray          jfloatArray;

110   typedef jarray          jdoubleArray;

111   typedef jobject         jthrowable;

112   typedef jobject         jweak;

113

114#endif/* not __cplusplus */

下面说几个重点的数据结构:

116struct _jfieldID;                       /* opaque structure */
117typedef struct _jfieldID* jfieldID;     /* field IDs */
119struct _jmethodID;                      /* opaque structure */
120typedef struct _jmethodID* jmethodID;   /* method IDs */
143typedef struct {
144    const char* name;       //对应Java层native函数的名,例如:processFile
145    const char* signature;  //函数的参数签名信息,使用JNI层定义的类型
146    void*       fnPtr;      // 函数指针,指向JNI层对应函数
147} JNINativeMethod;
149struct _JNIEnv;
150struct _JavaVM
151typedef const struct JNINativeInterface* C_JNIEnv;
153#if defined(__cplusplus)   // c++版本条件选择,决定JNIEnv和JavaVM结构体
154    typedef _JNIEnv JNIEnv;
155    typedef _JavaVM JavaVM;
156#else
157  typedef const struct JNINativeInterface* JNIEnv;  // 接口函数指针表
158  typedef const struct JNIInvokeInterface* JavaVM; // JNI调用接口,几个函数
159#endif
161/*
162 * Table of interface function pointers.
163 */
164struct JNINativeInterface {
212    jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
241    void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
306    jfieldID    (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
314    jlong       (*GetLongField)(JNIEnv*, jobject, jfieldID);
324    void        (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong);
390    jstring     (*NewStringUTF)(JNIEnv*, const char*);
392    /* JNI spec says this returns const jbyte*, but that's inconsistent */
393    const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
497}; // end 结构体
505struct _JNIEnv {
506    /* do not rename this; it does not seem to be entirely opaque */
507    const struct JNINativeInterface* functions;
// …
605    jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
606        { return functions->GetMethodID(this, clazz, name, sig); }
646    void CallVoidMethod(jobject obj, jmethodID methodID, ...)
647    {
648        va_list args;
649        va_start(args, methodID);
650        functions->CallVoidMethodV(this, obj, methodID, args);
651        va_end(args);
652    }
714    jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
715         { return functions->GetFieldID(this, clazz, name, sig); }
729    jlong GetLongField(jobject obj, jfieldID fieldID)
730         { return functions->GetLongField(this, obj, fieldID); }
750    void SetLongField(jobject obj, jfieldID fieldID, jlong value)
751         { functions->SetLongField(this, obj, fieldID, value); }
872    jstring NewStringUTF(const char* bytes)
873        { return functions->NewStringUTF(this, bytes); }
878    const char* GetStringUTFChars(jstring string, jboolean* isCopy)
879        { return functions->GetStringUTFChars(this, string, isCopy); }

} // end JNIEnv

1067 /* JNI invocation interface.JNI调用接口
1068 */
1069struct JNIInvokeInterface {
1070    void*       reserved0;
1071    void*       reserved1;
1072    void*       reserved2;
1074    jint        (*DestroyJavaVM)(JavaVM*);
1075    jint        (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);
1076    jint        (*DetachCurrentThread)(JavaVM*);
1077    jint        (*GetEnv)(JavaVM*, void**, jint);
1078    jint        (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
1079};
1081/*
1082 * C++ version.
1083 */
1084struct _JavaVM {
1085    const struct JNIInvokeInterface* functions;
1087#if defined(__cplusplus)
1088    jint DestroyJavaVM()
1089    { return functions->DestroyJavaVM(this); }
1090    jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
1091    { return functions->AttachCurrentThread(this, p_env, thr_args); }
1092    jint DetachCurrentThread()
1093    { return functions->DetachCurrentThread(this); }
1094    jint GetEnv(void** env, jint version)
1095    { return functions->GetEnv(this, env, version); }
1096    jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)
1097    { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
1098#endif /*__cplusplus*/
1099};

当Java层使用的参数不是基本类型,而是类对象时,在JNI层对应的jobject如何获取类对象的方法与成员变量?

定义的jFieldID和jmethodID就是用来在JNI层保存字段和方法,而这些字段和方法的获取是通过结构体 JNIEnv,其定义了一系列的JNI层函数指针方法。其中,GET开头的方法在JNI层获取对应的类型对象、方法等以供JNI层使用。也包含了某些SET方法。例如:

jlong GetLongField(jobject obj, jfieldID fieldID) ;

返回一个对象实例(非静态)域的值,长整型数域

void (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong);

 设置一个对象设置长整数域,即设置一个对象实例(非静态)域的值。

CallVoidMethod方法一般在JNI层需要native层反向回调Java层时使用。

JNIEnv是一个与线程相关的,每个线程对应各自的JNIEnv结构体,当java层native函数调用时,会转换成jni层函数,并由虚拟机传进来该JNIEnv结构体。有一种情况,当后台线程收到一个网络消息,且需要由Native层函数主动回调java层函数时,JNIEnv哪里来?我们不能保存另外一个线程的JNIEnv结构体,把它放到后台线程用。

  JavaVM结构体是虚拟机在JNI层叠代码,一个进程只有一个JavaVM对象,不论该进程有几个线程,该JavaVM只有一份,因此,进程的任何线程均可以使用,妙处就在这里,JavaVM结构体调用函数AttachCurrentThread 函数即可获取得到该线程的JNIEnv结构体对象。注意用完需要释放资源 ,调用DetachCurrentThread函数。

第三章 JNI通信Java层的实现

Java层通过JNI方式去调用Native层函数,必须先加载jni动态库,并初始化,书写响应的native函数,该函数对应于jni层相应的函数。

基本步骤就是上面提到的加载动态库、初始化、书写native函数。下面就以MediaScanner的源码为例,展开从Java到Native层整个过程的流程。本章介绍流程中的准备工作。

相应和Media相关的源码:

android8.1_trunk/frameworks/base/media/java/android/media/MediaPlayer.java

android8.1_trunk/frameworks/base/media/java/android/media/MediaScanner.java

1.       加载jni动态库并初始化

124public class MediaScanner implements AutoCloseable {
125    static {
126        System.loadLibrary("media_jni");
127        native_init();
128    }

上面是MediaScanner.java类加载动态库并初始化的实现,其实,在MediaPlayer.java中也包含了同样的静态快去加载”media_jni”动态库,并初始化。

2.       书写native函数

2078    private native void processDirectory(String path, MediaScannerClient client);
2079    private native void processFile(String path, String mimeType, MediaScannerClient client);
2080    private native void setLocale(String locale);
2081
2082    public native byte[] extractAlbumArt(FileDescriptor fd);
2083
2084    private static native final void native_init();
2085    private native final void native_setup();
2086    private native final void native_finalize();

  上面是MediaScanner.java定义的一些列native函数,该函数实际需要通过JNI通信调用jni函数,jni函数中和native层c/c++MediaScanner.cpp进行交互。

第四章 JNI层注册函数

  JNI函数的注册问题,十九上就是将Java层的native函数和JNI层对应的实现函数关联起来,有了关联,调用Java层的native函数时,就能转到JNI层对应的函数执行。JNI函数注册包括两种。

1.       静态注册

(1)    编写Java代码,编译生成.class文件

(2)    使用Javah程序,执行:javah –o output 包名.类名,生成output.h的JNI层头文件,声明了Java层native函数对应的JNI层函数,实现JNI函数即可。

例如:MediaScanner.java 所在包:android.media, 方法:

private native void processFile(String path, String mimeType, MediaScannerClient client);

生成:android_media_MediaScanner.h

  JNIEXPORT void JNICALL Java_android_media_MediaScanner_processFile

当Java层调用processFile函数时,会从JNI库找JNI函数,没找到会报错,找到会为这个processFile和JNI函数建立一个关联关系,保存JNI层该函数的函数指针。当再调用processFile,直接使用这个函数指针就可以了,这些是由虚拟机完成。

缺点如下:

(1)    编译所有声明native函数Java类,并使用Javah生成JNI头文件

(2)    javah生成的JNI层函数名较长,书写不方便

(3)    第一次调用native函数,根据函数名搜索对应的JNI层函数建立关联,会影响效率。

2.       动态注册

Java native函数是通过函数指针和JNI函数建立一一对应关系,如果有一个结构体来保存这个关联,直接使用该函数指针就可以了,对应的结构体: JNINativeMethod,该结构体是在第二章中jni.h头文件定义的。再来回顾一下:

143typedef struct {
144    const char* name;       //对应Java层native函数的名,例如:processFile
145    const char* signature;  //函数的参数签名信息,使用JNI层定义的类型
146    void*       fnPtr;      // 函数指针,指向JNI层对应函数
147} JNINativeMethod;

那么,该结构体是怎样具体使用把Java native函数和JNI层函数通过函数指针绑定到一起?

当Java层MediaScanner.java通过System.loadLibrary(“media_jni”)加载jni动态库后,紧接着会在该libmedia_jni.so库中去查找一个叫 JNI_OnLoad()的函数,如果找到就调用它,而动态注册的工作就是通过 JNI_OnLoad()函数来实现的。

通过查找,该函数位于如下android_media_MediaPlayer.cpp文件:

android8.1_trunk/frameworks/base/media/jni/android_media_MediaPlayer.cpp

来看下该函数的具体实现:

1464jintJNI_OnLoad(JavaVM*vm,void* /* reserved */)

1465{

1466   JNIEnv* env =NULL;

1467   jint result = -1;

1468   // 参数vm是虚拟机在JNI层的代表,每个java进程只一个

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

1470       ALOGE("ERROR: GetEnv failed\n");

1471       goto bail;

1472    }

1473   assert(env !=NULL);

1474

1475   if (register_android_media_ImageWriter(env) !=JNI_OK) {

1476       ALOGE("ERROR: ImageWriter native registration failed");

1477       goto bail;

1478    }

1479

1480   if (register_android_media_ImageReader(env) < 0) {

1481       ALOGE("ERROR: ImageReader native registration failed");

1482       goto bail;

1483    }

1484

1485   if (register_android_media_MediaPlayer(env) < 0) {

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

1487       goto bail;

1488    }

1489

1490   if (register_android_media_MediaRecorder(env) < 0) {

1491       ALOGE("ERROR: MediaRecorder native registration failed\n");

1492       goto bail;

1493    }

1494   // 动态注册MediaScanner的JNI函数

1495   if (register_android_media_MediaScanner(env) < 0) {

1496       ALOGE("ERROR: MediaScanner native registration failed\n");

1497       goto bail;

1498    }

1499

1500   if (register_android_media_MediaMetadataRetriever(env) < 0) {

1501       ALOGE("ERROR: MediaMetadataRetriever native registration failed\n");

1502       goto bail;

1503    }

1534

1535   if (register_android_media_MediaSync(env) < 0) {

1536       ALOGE("ERROR: MediaSync native registration failed");

1537       goto bail;

1538    }

1539

1540   if (register_android_media_MediaExtractor(env) < 0) {

1541       ALOGE("ERROR: MediaCodec native registration failed");

1542       goto bail;

1543    }

1555   if (register_android_media_Crypto(env) < 0) {

1556       ALOGE("ERROR: MediaCodec native registration failed");

1557       goto bail;

1558    }

1569

1570   if (register_android_media_MediaHTTPConnection(env) < 0) {

1571       ALOGE("ERROR: MediaHTTPConnection native registration failed");

1572       goto bail;

1573    }

1574

1575   /* success -- return valid version number */

1576   result = JNI_VERSION_1_4;

1577

1578bail:

1579   return result;

1580}

 JNI_OnLoad()函数注册了许多与media相关的JNI函数。也包含我们关注的MediaScanner注册函数register_android_media_MediaScanner(env),查看其它相关的与media相关的注册函数对应的java类,加载同一个动态库”media_jni”。再查看这些注册函数的声明,均是external类型,表面是外部全局的,其他文件也可访问。

1438// This function only registers the native methods
1439static int register_android_media_MediaPlayer(JNIEnv *env)
1440{
1441    return AndroidRuntime::registerNativeMethods(env,
1442                "android/media/MediaPlayer", gMethods, NELEM(gMethods));
1443}
1444extern int register_android_media_ImageReader(JNIEnv *env);
1453extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
1455extern int register_android_media_MediaRecorder(JNIEnv *env);
1456extern int register_android_media_MediaScanner(JNIEnv *env);
1462extern int register_android_mtp_MtpServer(JNIEnv *env);

  上面只是列出来部分,还有其他的注册函数,由于是在android_media_MediaPlayer.cpp内部,因此,关于MediPlayer的注册直接实现了,是使用AndroidRuntime的注册接口。那么,我们关注的MediaScanner的注册,应该也在对应的android_media_MediaScanner.cpp中具体实现。

android8.1_trunk/frameworks/base/media/jni/android_media_MediaScanner.cpp

459// This function only registers the native methods, and is called from
460// JNI_OnLoad in android_media_MediaPlayer.cpp
461int register_android_media_MediaScanner(JNIEnv *env)
462{
463    return AndroidRuntime::registerNativeMethods(env,
464                kClassMediaScanner, gMethods, NELEM(gMethods));
465}

  事实如此,在MediaScanner的JNI层cpp文件中,通过AndroidRuntime的注册方法实现。

关注参数kClassMediaScanner、gMethods,最后一个是方法个数。

  先来看一下android_media_MediaScanner.cpp的头文件等前面信息:

19#define LOG_TAG "MediaScannerJNI"
20#include <utils/Log.h>
21#include <utils/threads.h>
22#include <media/mediascanner.h>
23#include <media/stagefright/StagefrightMediaScanner.h>
24#include <private/media/VideoFrame.h>
25
26#include "jni.h"
27#include <nativehelper/JNIHelp.h>
28#include "android_runtime/AndroidRuntime.h"
29#include "android_runtime/Log.h"
30
31using namespace android;
32
33
34static const char* const kClassMediaScannerClient =
35        "android/media/MediaScannerClient";
36
37static const char* const kClassMediaScanner =
38        "android/media/MediaScanner";

// … 省略其他

  该信息包含了mediascanner.h、StagefrightMediaScanner.h、JNIHelp.h、AndroidRuntime.h等。

这些头文件的引入,我们可以在该c++文件中使用声明的类及方法。

  此外,还定义了常量字符串指针,字符串信息是对应 java层的类的结构路径。下面再来看看gMethods参数的具体信息:

415static const JNINativeMethod gMethods[] = {
416    {
417        "processDirectory",
418        "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
419        (void *)android_media_MediaScanner_processDirectory
420    },
421
422    {
423        "processFile",
424        "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
425        (void *)android_media_MediaScanner_processFile
426    },
427
428    {
429        "setLocale",
430        "(Ljava/lang/String;)V",
431        (void *)android_media_MediaScanner_setLocale
432    },
433
434    {
435        "extractAlbumArt",
436        "(Ljava/io/FileDescriptor;)[B",
437        (void *)android_media_MediaScanner_extractAlbumArt
438    },
439
440    {
441        "native_init",
442        "()V",
443        (void *)android_media_MediaScanner_native_init
444    },
445
446    {
447        "native_setup",
448        "()V",
449        (void *)android_media_MediaScanner_native_setup
450    },
451
452    {
453        "native_finalize",
454        "()V",
455        (void *)android_media_MediaScanner_native_finalize
456    },
457};

  是不是很惊讶,该JNINativeMethod结构体数组包含了MediaScanner.java中的所有native方法、JNI层参数签名以及在JNI层该方法对应的函数指针。现在,AndroidRuntime调用 registerNativeMethods()函数的参数清晰了,在继续往下分析前整理注册的思路:

Java层MediaScanner.java System.LoadLibrary()加载动态库  --- >

JNI层动态库查找JNI_OnLoad() 位于android_media_MediaPlayer.cpp并调用 -- >

JNI层android_media_MediaScanner.cpp 调用register_android_media_MediaScanner() -- >

JNI层AndroidRuntime.cpp调用

 registerNativeMethods(env,kClassMediaScanner, gMethods, NELEM(gMethods)) -- > 待续

  前面的分析到这里,继续往下分析,AndroidRuntime.cpp:

文件路径:android8.1_trunk/frameworks/base/core/jni/AndroidRuntime.cpp

282/*
283 * Register native methods using JNI.
284 */
285/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
286    const char* className, const JNINativeMethod* gMethods, int numMethods)
287{
288    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
289}

  调用jniRegisterNativeMethods()方法,它是 Android平台中为方便JNI使用由JNIHelp.cpp所提供的帮助函数。前面分析头文件时,已经include了JNIHelp.h,因此可直接调用。文件路径: android8.1_trunk/libnativehelper/JNIHelp.cpp

75extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
76    const JNINativeMethod* gMethods, int numMethods)
77{
78    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
80    ALOGV("Registering %s's %d native methods...", className, numMethods);
82    scoped_local_ref<jclass> c(env, findClass(env, className));
83    if (c.get() == NULL) {
         // 处理打印一些错误的信息
95    }
96
97    if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
         // 处理打印一些错误的信息
107    }
109    return 0;
110}

  这个帮助函数有两点较为重要:

(1)    scoped_local_ref<jclass> c(env, findClass(env, className));

获取Java层类名所对应的JNI层jclass对象

(2)    (*env)->RegisterNatives(e,c.get(), gMethods, numMethods)

通过JNIEnv结构体指针调用 RegisterNative()方法完成实际的java层到JNI层类、方法的关联,即完成了注册任务。

以MediaScanner.java为例,实际这两个步骤的参数如下:

className:  kClassMediaScanner  “android/media/MediaScanner”

gMethods:   指的定义的JNINativeMethod gMethods[]结构体数组,包含了processFile等。

 

第五章Java层与Native层通信

  第四章主要分析了注册JNI函数,建立java层native函数和JNI层函数的对应关系,通过函数指针保存指向JNI层函数,下次使用时,直接使用该函数指针就可以操作到JNI层函数。

  现在,当我们在Java层MediaScanner.java中调用其方法scanFile()来扫描文件时:

607        public void scanFile(String path, long lastModified, long fileSize,
608                boolean isDirectory, boolean noMedia) {
611            doScanFile(path, null, lastModified, fileSize, isDirectory, false, noMedia);
612        }
private native void processFile(String path, String mimeType, MediaScannerClient client);

该方法获取调用 doScanFile()函数,针对不同类型文件调用不同的处理函数,当文件是audio或者video时,会调用processFile()函数,该函数是native函数,由于MediaScanner.java已经加载过动态库并初始化以及完成了一些列java层native函数对应JNI层函数注册,当调用该processFile()时,JNI层android_media_MediaScanner.cpp中的JNI函数会通过函数指针被找到调用:android_media_MediaScanner_processFile()

android8.1_trunk/frameworks/base/media/jni/android_media_MediaScanner.cpp

267static void android_media_MediaScanner_processFile(
269        JNIEnv *env, jobject thiz, jstring path,
270        jstring mimeType, jobject client)
271{   // env便是运行当前线程的JNI环境JNIEnv, thiz 表示Java层MediaScanner对象
273    // 获取java层MediaScanner对象所对应Native层的MediaScanner.cpp类对象 
274    // Lock already hold by processDirectory
275    MediaScanner *mp = getNativeScanner_l(env, thiz);
276    if (mp == NULL) {
277        jniThrowException(env, kRunTimeException, "No scanner available");
278        return;
279    }
281    if (path == NULL) {
282        jniThrowException(env, kIllegalArgumentException, NULL);
283        return;
284    }
285    // 将 JNI类型jstring对象转化为char* 字符串指针类型
286    const char *pathStr = env->GetStringUTFChars(path, NULL);
287    if (pathStr == NULL) {  // Out of memory
288        return;
289    }
291    const char *mimeTypeStr =
292        (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
293    if (mimeType && mimeTypeStr == NULL) {  // Out of memory
294        // ReleaseStringUTFChars can be called with an exception pending.
295        env->ReleaseStringUTFChars(path, pathStr);
296        return;
297    }
298    // 创建 MediaScannerClient.cpp的子类对象,涉及到 JNIEnv操jobject
299    MyMediaScannerClient myClient(env, client);
300    MediaScanResult result = mp->processFile(pathStr, mimeTypeStr, myClient);
301    if (result == MEDIA_SCAN_RESULT_ERROR) {
302        ALOGE("An error occurred while scanning file '%s'.", pathStr);
303    }
304    env->ReleaseStringUTFChars(path, pathStr);
305    if (mimeType) {
306        env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
307    }
308}

先看下如下getNativeScanner_l()方法:

228static MediaScanner *getNativeScanner_l(JNIEnv* env, jobject thiz){
230    return (MediaScanner *) env->GetLongField(thiz, fields.context);
231}
   // 看来native_setup调用,就会创建Native层的MediaScanner.cpp子类对象,并将该对象mp
   // 通过绑定thiz、fields.context设置进去,以便通过相同参数获取。
389static void
390android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz){
393    MediaScanner *mp = new StagefrightMediaScanner;
       // 设置长整数域,即设置一个对象实例(非静态)域的值
400    env->SetLongField(thiz, fields.context, (jlong)mp);
401}
thiz 对象表示Java层的MediaScanner实例对象;
fields.context对象是当java层native_init()调用时在jni层函数初始化的: 
class clazz = env->FindClass(kClassMediaScanner);
fields.context = env->GetFieldID(clazz, "mNativeContext", "J");

  Java层MediaScanner的native_setup()函数在构造器中调用。因此,创建java对象也就立即创建了Native层对应的StagefrightMediaScanner对象。

JNI层函数android_media_MediaScanner_processFile()的关键有如下几点:

(1)    获取对象MediaScanner *mp = getNativeScanner_l(env, thiz);

调用的JNIEnv的GETLongField()获取长整数型的一个对象实例mp。

(2)    创建 MyMediaScannerClient myClient(env, client);

具体查看该构造器,将 java层MediaScannerClient对应的方法获取,保存到JNI层的对象中,以便以后使用。

111    MyMediaScannerClient(JNIEnv *env, jobject client)
112        :   mEnv(env),
113            mClient(env->NewGlobalRef(client))
117    {
118        ALOGV("MyMediaScannerClient constructor");
119        jclass mediaScannerClientInterface =
120                env->FindClass(kClassMediaScannerClient);
125            mScanFileMethodID = env->GetMethodID(
126                                    mediaScannerClientInterface,
127                                    "scanFile",
128                                    "(Ljava/lang/String;JJZZ)V");
135            mSetMimeTypeMethodID = env->GetMethodID(
136                                    mediaScannerClientInterface,
137                                    "setMimeType",
138                                    "(Ljava/lang/String;)V");
140    }

当构造器初始化这些对象后,在哪里使用?在MyMediaScannerClient类内部定义如下函数:

148    virtual status_t scanFile(const char* path, long long lastModified,
149            long long fileSize, bool isDirectory, bool noMedia)
150    {
151        ALOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)",
152            path, lastModified, fileSize, isDirectory);
154        jstring pathStr;
155        if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
156            mEnv->ExceptionClear();
157            return NO_MEMORY;
158        }
159      // JNI层调用,反向调用到java层MediaScannerClient类的 scanFile()方法
160        mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
161                fileSize, isDirectory, noMedia);
163        mEnv->DeleteLocalRef(pathStr);
164        return checkAndClearExceptionFromCallback(mEnv, "scanFile");
165    }
204    virtual status_t setMimeType(const char* mimeType)
205    {
206        ALOGV("setMimeType: %s", mimeType);
207        jstring mimeTypeStr;
208        if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) {
209            mEnv->ExceptionClear();
210            return NO_MEMORY;
211        }
212     // JNI层调用,实现调用Java层MediaScannerClient类的 setMimeType()方法
213        mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr);
214
215        mEnv->DeleteLocalRef(mimeTypeStr);
216        return checkAndClearExceptionFromCallback(mEnv, "setMimeType");
217    }

这个MyMediaScannerClient的c++类的scanFile()和setMimeType()函数是在Native层MediaScanner.cpp被调用的。

(3)    Native层MediaScanner调用 mp->processFile(pathStr, mimeTypeStr, myClient);

这个方法很重要,Native层调用自己的processFile函数,即StagefrightMediaScanner该类的方法。

android8.1_trunk/frameworks/av/media/libstagefright/StagefrightMediaScanner.cpp#58

processFile(constchar *path, const char *mimetype, MediaScannerClient &client);

调用如下方法,完成实际的工作:

70MediaScanResultStagefrightMediaScanner::processFileInternal(

71        const char *path, const char */* mimeType */,

72        MediaScannerClient &client) {

73    const char *extension =strrchr(path,'.');

75    if (!extension) {

76        return MEDIA_SCAN_RESULT_SKIPPED;

77    }

79    if (!FileHasAcceptableExtension(extension)) {

80        return MEDIA_SCAN_RESULT_SKIPPED;

81    }

83    sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever);

85    int fd = open(path, O_RDONLY | O_LARGEFILE);

86    status_t status;

87    if (fd < 0) {

88        // couldn't open it locally, maybe the media server can?

89        sp<IMediaHTTPService> nullService;

90        status = mRetriever->setDataSource(nullService,path);

91    } else {

92        status = mRetriever->setDataSource(fd, 0,0x7ffffffffffffffL);

93        close(fd);

94    }

96    if (status) {

97        return MEDIA_SCAN_RESULT_ERROR;

98    }

100    const char *value;

101    if ((value = mRetriever->extractMetadata(

102                   METADATA_KEY_MIMETYPE)) != NULL) {

103       status =client.setMimeType(value);// 这个地方就要去掉Java层的啦

104       if (status) {

105           return MEDIA_SCAN_RESULT_ERROR;

106        }

107    }

109    struct KeyMap {

110        const char *tag;

111       int key;

112    };

113    static const KeyMap kKeyMap[] = {

114        {"tracknumber", METADATA_KEY_CD_TRACK_NUMBER },

115        {"discnumber", METADATA_KEY_DISC_NUMBER },

116        {"album", METADATA_KEY_ALBUM },

117        {"artist", METADATA_KEY_ARTIST },

118        {"albumartist", METADATA_KEY_ALBUMARTIST },

119        {"composer", METADATA_KEY_COMPOSER },

120        { "genre", METADATA_KEY_GENRE },

121        {"title", METADATA_KEY_TITLE },

122        {"year", METADATA_KEY_YEAR },

123        {"duration", METADATA_KEY_DURATION },

124        {"writer", METADATA_KEY_WRITER },

125        {"compilation", METADATA_KEY_COMPILATION },

126        {"isdrm", METADATA_KEY_IS_DRM },

127        {"date", METADATA_KEY_DATE },

128        {"width", METADATA_KEY_VIDEO_WIDTH },

129        {"height", METADATA_KEY_VIDEO_HEIGHT },

130    };

131   static const size_t kNumEntries = sizeof(kKeyMap) /sizeof(kKeyMap[0]);

133   for (size_t i = 0; i < kNumEntries; ++i) {

134       const char *value;

135       if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) !=NULL) {

136           status = client.addStringTag(kKeyMap[i].tag,value);

137           if (status !=OK) {

138               return MEDIA_SCAN_RESULT_ERROR;

139            }

140        }

141    }

143    return MEDIA_SCAN_RESULT_OK;

144}

  我们看到 103    status =client.setMimeType(value);// 这个地方就要去掉Java层的啦

再来到Java层,看看是不是有这个函数?

android8.1_trunk/frameworks/base/media/java/android/media/MediaScanner.java

500    protected class MyMediaScannerClient implements MediaScannerClient
889        public void setMimeType(String mimeType) {
890            if ("audio/mp4".equals(mMimeType) &&
891                    mimeType.startsWith("video")) {
892                // for feature parity with Donut, we force m4a files to keep the
893                // audio/mp4 mimetype, even if they are really "enhanced podcasts"
894                // with a video track
895                return;
896            }
897            mMimeType = mimeType;
898            mFileType = MediaFile.getFileTypeForMimeType(mimeType);
899        }

      }

  除了这个函数,还包括其他的许多函数。至此,我们看到了JNI通信的所有涉及到过程知识了。


 本文参考了《深入理解Android卷一》,转载请标明出处!

如果喜欢的话,欢迎到如下下载文档!

http://download.csdn.net/download/snail201211/10159467


猜你喜欢

转载自blog.csdn.net/snail201211/article/details/78809249