JNI
java native interface
含义: java语言 与 native语言 之间的接口
1) java中调用native接口
2) native语言中访用java中的变量和方法
==================
================== 1. java调用native接口 ==========
==================
1) 在.java文件中使用native关键字声明方法,如 android.media.MediaScanner类中:
private static native final void native_init();
private native void processFile(String path,String mimeType,MediaScannerClient client);
2) 在.java中使用静态代码块加载对应的JNI库.
static {
System.loadLibrary("media_jni");
}
--------- 问题:
1) 在java中调用native关键字定义的方法,jvm按照什么规则找到对应的native实现呢?
native方法归根到底也是一段代码,是c/c++的一个函数,只要找到函数的入口就可以啦!!
函数的入口自然要根据函数的名称来查找了,所以这个规则即是native方法的命名规则.
---------- native方法的默认命名规则,即静态注册JNI函数的方法 -------
使用javah工具程序: javah -o [output] [packagename.classname] 例如: javah -o myjnitest.h com.example.cn.ljc.demo.jni.MainActivity
****** 以"Java_"开头,接着是java函数的全路径名,然后将所有的"."替换为"_";
****** 如果Java层函数名中如果有一个"_",转换成jni后变为"_1"
如android.media.MediaScanner类中native_init方法:
JNIEXPORT void JNICALL Java_android_media_MediaScanner_native_1init //Java层函数名中如果有一个"_",转换成jni后变为"_1"
2) 为什么写成: System.loadLibrary("media_jni"); //系统会根据 不同的平台拓展成真实的动态库文件名,如windows平台下拓展为media_jni.dll,linux平台下拓展为media_jni.so
3) JNI函数的动态注册方法:
当Java层通过System.loadLibrary加载完JNI动态库后,紧接着会查找该库中一个叫JNI_OnLoad的函数.想使用动态注册方法,就必须实现JNI_OnLoad函数.如media_jni.so中
---------------- android_media_MediaPlayer.cpp ---> JNI_OnLoad ---------
./frameworks/base/media/jni/android_media_MediaPlayer.cpp
------
.........
jint JNI_OnLoad(JavaVM* vm, void* reserved) //传递参数JavaVM
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { //从VM中取得JNIEnv的指针
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;
}
.........
----------------- android_media_MediaPlayer.cpp ---> register_android_media_MediaPlayer(env) 方法 ---------
./frameworks/base/media/jni/android_media_MediaPlayer.cpp
------
static int register_android_media_MediaPlayer(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
------------ Mediaplayer.cpp中gMethods的定义: //是JNINativeMethod结构体
static JNINativeMethod gMethods[] = {
{"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource},
// ========== java类中的方法名 //参数列表和返回值 //c++/c中的方法名
{
"_setDataSource",
"(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
(void *)android_media_MediaPlayer_setDataSourceAndHeaders
},
{"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface},
{"prepare", "()V", (void *)android_media_MediaPlayer_prepare},
{"prepare", "()V", (void *)android_media_MediaPlayer_prepare},
{"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
{"_start", "()V", (void *)android_media_MediaPlayer_start},
{"_stop", "()V", (void *)android_media_MediaPlayer_stop},
{"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt},
{"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
{"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter},
{"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata},
.....}
----------------- AndroidRuntime ---> regsterNativeMethods ---------------
./frameworks/base/core/jni/AndroidRuntime.cpp
./frameworks/base/include/android_runtime/AndroidRuntime.h
------
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* className, const JNINativeMethod* gMethods, int numMethods) //第二个参数:className指定了java类路径,gMethods描述了方法的映射过程.
{
return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
------------ JNIHelp.c ---> jniRegisterNativeMethods ----------
./dalvik/libnativehelper/include/nativehelper/JNIHelp.h
./dalvik/libnativehelper/JNIHelp.cpp
-------
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
LOGV("Registering %s natives", className);
scoped_local_ref<jclass> c(env, findClass(env, className));
if (c.get() == NULL) {
LOGE("Native registration unable to find class '%s', aborting", className);
abort();
}
if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { //调用JNIEnv的RegisterNatives方法.
LOGE("RegisterNatives failed for '%s', aborting", className);
abort();
}
return 0;
}
====================== //关于gMethods中第二个参数的说明: ======================
1) "()" 中的字符表示参数列表,后面的则代表返回值。例如"()V" 就表示void Func(); "(II)V" 表示 void Func(int, int);
2) 如果Java函数的参数是class,则以"L"开头,以";"结尾,中间是用"/" 隔开的包及类名。
具体的每一个字符的对应关系如下:
字符 native类型 Java类型
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
数组则以"["开始,用两个字符表示
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]
====================== java数据类型与native数据类型的转换 ======================
------ 基本数据类型 ------
void void
boolean jboolean
int jint
long jlong
double jdouble
float jfloat
jbyte jbyte
char jchar
short jshort
------ 引用数据类型 ------
int[] jintArray
float[] jfloatArray
byte[] jbyteArray
char[] jcharArray
short[] jshortArray
double[] jdoubleArray
long[] jlongArray
boolean[] jbooleanArray
java.lang.Class jobject
java.lang.String jstring
All objects jobject
Object[] jobjectArray
java.lang.Throwable jthrowable
例如 //MediaScanner.java (base\media\java\android\media) 84154 2012-11-27
private native void processFile(String path, String mimeType, MediaScannerClient client);
//android_media_MediaScanner.cpp (base\media\jni) 14489 2012-11-27
static void android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path,jstring mimeType, jobject client)
==================
================== 2. native语言中访用java中的变量和方法 ==========
==================
这一切都要信赖于JVM提供的JNI环境: JNIEnv.JNIEnv是一个定义在jni.h中的结构体,提供了众多调用Java接口的方法:
总结其原理:C/C++要调用JAVA程序,必须先加载JAVA虚拟机,由JAVA虚拟机解释执行class文件。为了初始化JAVA虚拟机,JNI提供了一系列的接口函数,通过这些函数方便地加载虚拟机到内存中。
1). 加载虚拟机:
函数:jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void args);
参数说明:JavaVM **pvm JAVA虚拟机指针,第二个参数JNIEnv *env是贯穿整个调用过程的一个参数,因为后面的所有函数都需要这个参数,需注意的是第三个参数,在jdk1.1与1.2版本有些不同,在JDK 1.1中第三个参数总是指向一个结构JDK1_ 1InitArgs,这个结构无法完全在所有版本的虚拟机中进行无缝移植。所以为了保证可移植性,建议使用jdk1.2的方法加载虚拟机。
2). 获取指定对象的类定义:
有两种方法可获得类定义,一是在已知类名的情况使用FindClass来获取;二是通过对象直接得到类定义GetObjectClass
3). 获取要调用的方法:
获得非静态方法:
jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig);
获得静态方法:
jmethodID (JNICALL *GetStaticMethodID)(JNIEnv *env, jclass class, const char *name, const char *sig);
参数说明:JNIEnv *env初始化是得到的JNI环境;jclass class前面已获取到的类定义;const char *name方法名;const char *sig方法参数定义
4). 调用JAVA类方法:
函数:CallObjectMethod(JNIEnv *env, jobject obj, jmethodID mid);
函数:CallStaticObjectMethod((JNIEnv *env, jobject obj, jmethodID mid);
5). 获得类属性的定义:
jfieldID (JNICALL *GetFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
静态属性:
jfieldID (JNICALL *GetStaticFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
6). 数组处理:
要创建数组首先要知道类型及长度,JNI提供了一系列的数组类型及操作的函数如:
NewIntArray、NewLongArray、NewShortArray、NewFloatArray、NewDoubleArray、 NewBooleanArray、NewStringUTF、NewCharArray、NewByteArray、NewString,访问通过 GetBooleanArrayElements、GetIntArrayElements等函数。
7). 异常:
由于调用了Java的方法,会产生异常。这些异常在C/C++中无法通过本身的异常处理机制来捕捉到,但可以通过JNI一些函数来获取Java中抛出的异常信息。
8).多线程调用
我们知道JAVA是非常消耗内存的,我们希望在多线程中能共享一个JVM虚拟机,真正消耗大量系统资源的是JAVA虚拟机jvm而不是虚拟机环境 env,jvm是允许多个线程访问的,但是虚拟机环境只能被创建它本身的线程所访问,而且每个线程必须创建自己的虚拟机环境env。JNI提供了两个函 数:AttachCurrentThread和DetachCurrentThread。便于子线程创建自己的虚拟机环境。
--------------- native方法中调用java方法 --------- 例子:
1) jfieldID GetFieldID(jclass clazz, const char* name, const char* sig) { return functions->GetFieldID(this, clazz, name, sig); }
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) { return functions->GetMethodID(this, clazz, name, sig); }
2) ------------- frameworks/base/media/jni/android_media_MediaScanner.cpp ---> MyMediaScannerClient构造器 -------
MyMediaScannerClient(JNIEnv *env, jobject client) : mEnv(env), mClient(env->NewGlobalRef(client)),
mScanFileMethodID(0), mHandleStringTagMethodID(0), mSetMimeTypeMethodID(0)
{
jclass mediaScannerClientInterface = env->FindClass(kClassMediaScannerClient);
if (mediaScannerClientInterface == NULL) {
LOGE("Class %s not found", kClassMediaScannerClient);
"scanFile",
"(Ljava/lang/String;JJZZ)V");
...... }
}
3) ---------- frameworks/base/media/jni/android_media_MediaScanner.cpp ---> scanFile 方法 ---------
virtual status_t scanFile(const char* path, long long lastModified,
long long fileSize, bool isDirectory, bool noMedia)
{
jstring pathStr;
if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
mEnv->ExceptionClear(); return NO_MEMORY;
}
//调用CallVoidMethod,mClient对象的mScanFileMethodID方法,传递参数pathSrc等
mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
fileSize, isDirectory, noMedia);
mEnv->DeleteLocalRef(pathStr);
return checkAndClearExceptionFromCallback(mEnv, "scanFile");
}
java native interface
含义: java语言 与 native语言 之间的接口
1) java中调用native接口
2) native语言中访用java中的变量和方法
==================
================== 1. java调用native接口 ==========
==================
1) 在.java文件中使用native关键字声明方法,如 android.media.MediaScanner类中:
private static native final void native_init();
private native void processFile(String path,String mimeType,MediaScannerClient client);
2) 在.java中使用静态代码块加载对应的JNI库.
static {
System.loadLibrary("media_jni");
}
--------- 问题:
1) 在java中调用native关键字定义的方法,jvm按照什么规则找到对应的native实现呢?
native方法归根到底也是一段代码,是c/c++的一个函数,只要找到函数的入口就可以啦!!
函数的入口自然要根据函数的名称来查找了,所以这个规则即是native方法的命名规则.
---------- native方法的默认命名规则,即静态注册JNI函数的方法 -------
使用javah工具程序: javah -o [output] [packagename.classname] 例如: javah -o myjnitest.h com.example.cn.ljc.demo.jni.MainActivity
****** 以"Java_"开头,接着是java函数的全路径名,然后将所有的"."替换为"_";
****** 如果Java层函数名中如果有一个"_",转换成jni后变为"_1"
如android.media.MediaScanner类中native_init方法:
JNIEXPORT void JNICALL Java_android_media_MediaScanner_native_1init //Java层函数名中如果有一个"_",转换成jni后变为"_1"
2) 为什么写成: System.loadLibrary("media_jni"); //系统会根据 不同的平台拓展成真实的动态库文件名,如windows平台下拓展为media_jni.dll,linux平台下拓展为media_jni.so
3) JNI函数的动态注册方法:
当Java层通过System.loadLibrary加载完JNI动态库后,紧接着会查找该库中一个叫JNI_OnLoad的函数.想使用动态注册方法,就必须实现JNI_OnLoad函数.如media_jni.so中
---------------- android_media_MediaPlayer.cpp ---> JNI_OnLoad ---------
./frameworks/base/media/jni/android_media_MediaPlayer.cpp
------
.........
jint JNI_OnLoad(JavaVM* vm, void* reserved) //传递参数JavaVM
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { //从VM中取得JNIEnv的指针
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;
}
.........
----------------- android_media_MediaPlayer.cpp ---> register_android_media_MediaPlayer(env) 方法 ---------
./frameworks/base/media/jni/android_media_MediaPlayer.cpp
------
static int register_android_media_MediaPlayer(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
------------ Mediaplayer.cpp中gMethods的定义: //是JNINativeMethod结构体
static JNINativeMethod gMethods[] = {
{"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource},
// ========== java类中的方法名 //参数列表和返回值 //c++/c中的方法名
{
"_setDataSource",
"(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
(void *)android_media_MediaPlayer_setDataSourceAndHeaders
},
{"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface},
{"prepare", "()V", (void *)android_media_MediaPlayer_prepare},
{"prepare", "()V", (void *)android_media_MediaPlayer_prepare},
{"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
{"_start", "()V", (void *)android_media_MediaPlayer_start},
{"_stop", "()V", (void *)android_media_MediaPlayer_stop},
{"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt},
{"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
{"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter},
{"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata},
.....}
----------------- AndroidRuntime ---> regsterNativeMethods ---------------
./frameworks/base/core/jni/AndroidRuntime.cpp
./frameworks/base/include/android_runtime/AndroidRuntime.h
------
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* className, const JNINativeMethod* gMethods, int numMethods) //第二个参数:className指定了java类路径,gMethods描述了方法的映射过程.
{
return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
------------ JNIHelp.c ---> jniRegisterNativeMethods ----------
./dalvik/libnativehelper/include/nativehelper/JNIHelp.h
./dalvik/libnativehelper/JNIHelp.cpp
-------
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
LOGV("Registering %s natives", className);
scoped_local_ref<jclass> c(env, findClass(env, className));
if (c.get() == NULL) {
LOGE("Native registration unable to find class '%s', aborting", className);
abort();
}
if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { //调用JNIEnv的RegisterNatives方法.
LOGE("RegisterNatives failed for '%s', aborting", className);
abort();
}
return 0;
}
====================== //关于gMethods中第二个参数的说明: ======================
1) "()" 中的字符表示参数列表,后面的则代表返回值。例如"()V" 就表示void Func(); "(II)V" 表示 void Func(int, int);
2) 如果Java函数的参数是class,则以"L"开头,以";"结尾,中间是用"/" 隔开的包及类名。
具体的每一个字符的对应关系如下:
字符 native类型 Java类型
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
数组则以"["开始,用两个字符表示
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]
====================== java数据类型与native数据类型的转换 ======================
------ 基本数据类型 ------
void void
boolean jboolean
int jint
long jlong
double jdouble
float jfloat
jbyte jbyte
char jchar
short jshort
------ 引用数据类型 ------
int[] jintArray
float[] jfloatArray
byte[] jbyteArray
char[] jcharArray
short[] jshortArray
double[] jdoubleArray
long[] jlongArray
boolean[] jbooleanArray
java.lang.Class jobject
java.lang.String jstring
All objects jobject
Object[] jobjectArray
java.lang.Throwable jthrowable
例如 //MediaScanner.java (base\media\java\android\media) 84154 2012-11-27
private native void processFile(String path, String mimeType, MediaScannerClient client);
//android_media_MediaScanner.cpp (base\media\jni) 14489 2012-11-27
static void android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path,jstring mimeType, jobject client)
==================
================== 2. native语言中访用java中的变量和方法 ==========
==================
这一切都要信赖于JVM提供的JNI环境: JNIEnv.JNIEnv是一个定义在jni.h中的结构体,提供了众多调用Java接口的方法:
总结其原理:C/C++要调用JAVA程序,必须先加载JAVA虚拟机,由JAVA虚拟机解释执行class文件。为了初始化JAVA虚拟机,JNI提供了一系列的接口函数,通过这些函数方便地加载虚拟机到内存中。
1). 加载虚拟机:
函数:jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void args);
参数说明:JavaVM **pvm JAVA虚拟机指针,第二个参数JNIEnv *env是贯穿整个调用过程的一个参数,因为后面的所有函数都需要这个参数,需注意的是第三个参数,在jdk1.1与1.2版本有些不同,在JDK 1.1中第三个参数总是指向一个结构JDK1_ 1InitArgs,这个结构无法完全在所有版本的虚拟机中进行无缝移植。所以为了保证可移植性,建议使用jdk1.2的方法加载虚拟机。
2). 获取指定对象的类定义:
有两种方法可获得类定义,一是在已知类名的情况使用FindClass来获取;二是通过对象直接得到类定义GetObjectClass
3). 获取要调用的方法:
获得非静态方法:
jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig);
获得静态方法:
jmethodID (JNICALL *GetStaticMethodID)(JNIEnv *env, jclass class, const char *name, const char *sig);
参数说明:JNIEnv *env初始化是得到的JNI环境;jclass class前面已获取到的类定义;const char *name方法名;const char *sig方法参数定义
4). 调用JAVA类方法:
函数:CallObjectMethod(JNIEnv *env, jobject obj, jmethodID mid);
函数:CallStaticObjectMethod((JNIEnv *env, jobject obj, jmethodID mid);
5). 获得类属性的定义:
jfieldID (JNICALL *GetFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
静态属性:
jfieldID (JNICALL *GetStaticFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
6). 数组处理:
要创建数组首先要知道类型及长度,JNI提供了一系列的数组类型及操作的函数如:
NewIntArray、NewLongArray、NewShortArray、NewFloatArray、NewDoubleArray、 NewBooleanArray、NewStringUTF、NewCharArray、NewByteArray、NewString,访问通过 GetBooleanArrayElements、GetIntArrayElements等函数。
7). 异常:
由于调用了Java的方法,会产生异常。这些异常在C/C++中无法通过本身的异常处理机制来捕捉到,但可以通过JNI一些函数来获取Java中抛出的异常信息。
8).多线程调用
我们知道JAVA是非常消耗内存的,我们希望在多线程中能共享一个JVM虚拟机,真正消耗大量系统资源的是JAVA虚拟机jvm而不是虚拟机环境 env,jvm是允许多个线程访问的,但是虚拟机环境只能被创建它本身的线程所访问,而且每个线程必须创建自己的虚拟机环境env。JNI提供了两个函 数:AttachCurrentThread和DetachCurrentThread。便于子线程创建自己的虚拟机环境。
--------------- native方法中调用java方法 --------- 例子:
1) jfieldID GetFieldID(jclass clazz, const char* name, const char* sig) { return functions->GetFieldID(this, clazz, name, sig); }
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) { return functions->GetMethodID(this, clazz, name, sig); }
2) ------------- frameworks/base/media/jni/android_media_MediaScanner.cpp ---> MyMediaScannerClient构造器 -------
MyMediaScannerClient(JNIEnv *env, jobject client) : mEnv(env), mClient(env->NewGlobalRef(client)),
mScanFileMethodID(0), mHandleStringTagMethodID(0), mSetMimeTypeMethodID(0)
{
jclass mediaScannerClientInterface = env->FindClass(kClassMediaScannerClient);
if (mediaScannerClientInterface == NULL) {
LOGE("Class %s not found", kClassMediaScannerClient);
} else {
//获取mediaScannerClientInterface类的scanFile方法,方法签名为最后一个参数
mScanFileMethodID = env->GetMethodID(
mediaScannerClientInterface,"scanFile",
"(Ljava/lang/String;JJZZ)V");
...... }
}
3) ---------- frameworks/base/media/jni/android_media_MediaScanner.cpp ---> scanFile 方法 ---------
virtual status_t scanFile(const char* path, long long lastModified,
long long fileSize, bool isDirectory, bool noMedia)
{
jstring pathStr;
if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
mEnv->ExceptionClear(); return NO_MEMORY;
}
//调用CallVoidMethod,mClient对象的mScanFileMethodID方法,传递参数pathSrc等
mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
fileSize, isDirectory, noMedia);
mEnv->DeleteLocalRef(pathStr);
return checkAndClearExceptionFromCallback(mEnv, "scanFile");
}
================ JNIEnv提供的方法很多,上面提到的只是一部分,具体可查看 jni.h中JNIEnv结构体的定义 ------ //./dalvik/libnativehelper/include/nativehelper/jni.h