AndroidO 平台JNI机制的学习
来源:互联网 发布:python 完成端口 编辑:程序博客网 时间:2024/06/05 18:55
第一章 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 typedefuint8_t jboolean; /* unsigned 8 bits */
36 typedefint8_t jbyte; /* signed 8 bits */
37 typedefuint16_t jchar; /* unsigned 16 bits */
38 typedefint16_t jshort; /* signed 16 bits */
39 typedefint32_t jint; /* signed 32 bits */
40 typedefint64_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 typedefjobject jclass;
100 typedefjobject jstring;
101 typedefjobject jarray;
102 typedefjarray jobjectArray;
103 typedefjarray jbooleanArray;
104 typedefjarray jbyteArray;
105 typedefjarray jcharArray;
106 typedefjarray jshortArray;
107 typedefjarray jintArray;
108 typedefjarray jlongArray;
109 typedefjarray jfloatArray;
110 typedefjarray jdoubleArray;
111 typedefjobject jthrowable;
112 typedefjobject 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 jintresult = -1;
1468 // 参数vm是虚拟机在JNI层的代表,每个java进程只一个
1469 if (vm->GetEnv((void**) &env,JNI_VERSION_1_4) !=JNI_OK) {
1470 ALOGE("ERROR: GetEnv failed\n");
1471 gotobail;
1472 }
1473 assert(env !=NULL);
1474
1475 if (register_android_media_ImageWriter(env) !=JNI_OK) {
1476 ALOGE("ERROR: ImageWriter native registration failed");
1477 gotobail;
1478 }
1479
1480 if (register_android_media_ImageReader(env) < 0) {
1481 ALOGE("ERROR: ImageReader native registration failed");
1482 gotobail;
1483 }
1484
1485 if (register_android_media_MediaPlayer(env) < 0) {
1486 ALOGE("ERROR: MediaPlayer native registration failed\n");
1487 gotobail;
1488 }
1489
1490 if (register_android_media_MediaRecorder(env) < 0) {
1491 ALOGE("ERROR: MediaRecorder native registration failed\n");
1492 gotobail;
1493 }
1494 // 动态注册MediaScanner的JNI函数
1495 if (register_android_media_MediaScanner(env) < 0) {
1496 ALOGE("ERROR: MediaScanner native registration failed\n");
1497 gotobail;
1498 }
1499
1500 if (register_android_media_MediaMetadataRetriever(env) < 0) {
1501 ALOGE("ERROR: MediaMetadataRetriever native registration failed\n");
1502 gotobail;
1503 }
1534
1535 if (register_android_media_MediaSync(env) < 0) {
1536 ALOGE("ERROR: MediaSync native registration failed");
1537 gotobail;
1538 }
1539
1540 if (register_android_media_MediaExtractor(env) < 0) {
1541 ALOGE("ERROR: MediaCodec native registration failed");
1542 gotobail;
1543 }
1555 if (register_android_media_Crypto(env) < 0) {
1556 ALOGE("ERROR: MediaCodec native registration failed");
1557 gotobail;
1558 }
1569
1570 if (register_android_media_MediaHTTPConnection(env) < 0) {
1571 ALOGE("ERROR: MediaHTTPConnection native registration failed");
1572 gotobail;
1573 }
1574
1575 /* success -- return valid version number */
1576 result =JNI_VERSION_1_4;
1577
1578bail:
1579 returnresult;
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 returnMEDIA_SCAN_RESULT_ERROR;
106 }
107 }
109 struct KeyMap {
110 const char *tag;
111 intkey;
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 constsize_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 returnMEDIA_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
- AndroidO 平台JNI机制的学习
- OPhone平台的JNI机制探索
- 基于androidO的gralloc 流程分析
- AndroidO中DRM的工作流程
- android平台的jni
- Android平台Native开发与JNI机制
- Android平台Native开发与JNI机制
- android平台的jni,添加
- OPhone平台Native开发与JNI机制详解
- Android平台Native开发与JNI机制详解
- Android平台Native开发与JNI机制详解
- Android平台Native开发与JNI机制详解
- Android平台Native开发与JNI机制详解
- Android平台Native开发与JNI机制详解
- Android平台Native开发与JNI机制详解
- Android平台Native开发与JNI机制详解
- Android平台Native开发与JNI机制详解
- Android平台Native开发与JNI机制详解
- 19.springboot发送mail
- 为什么上面这段代码没有直接写的 function add (){...} 而是把function赋值给了变量add呢?
- Tomcat优化
- 分类算法之朴素贝叶斯分类(Naive Bayesian classification)
- GIT 检查、撤销修改简明教程
- AndroidO 平台JNI机制的学习
- css3+jq制作百分比圆环,扇形图(两种颜色)
- JSON.parse()和JSON.stringify()
- 十进制负数转化成二进制
- java面试题(二)------阿里mysql
- maven中跳转页面需要注意的事项
- 从决策树学习谈到贝叶斯分类算法、EM、HMM
- ldap懒加载
- ubuntu16.04 mysql5.7.20表中插入中文显示???的解决方法