您的位置:首页 > 移动开发 > Android开发

AndroidO 平台JNI机制的学习

2017-12-15 09:12 423 查看
第一章 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: