概述
** JNI (Java Native Interface)是帮助Java调用Native库的桥梁**
图解如下
JNI 是通过C/C++
实现的一层桥梁。
那么Native 世界是什么呢?说简单一点就是C/C++
底层世界。
为什么需要这一层呢?
以我现在的理解是,在两个不同的世界直接通讯是可行的,但是会很麻烦,因为你每一次通讯都必须讲两个世界的语言翻译一下。如果我们实现一个大家公认的翻译程序,那么到时候只需调用一下这些已经定义好的接口就行了!这就是JNI也叫Java 和 Native 之间的接口。
既然知道他是什么之后,我们要开始了解他是真么运行的!
实例
注意:名字是media
,前面的lib
和.so
是拓展名,如果是在windows 下拓展名是libmeida.dll
MediaScanner.java
目录frameworks/base/media/java/android/meidia/MediaScanner.java
public class MediaScanner {
static {
/**加载对应的JNI库,media_jni是对应JNI库的名字,实际上加载动态库的时候他 会拓展成libmedia_jni.so **/
System.loadLibrary("media_jni"); native_init(); //调用native_init()函数,他是一个native函数
}
......
//非native函数
public void scanDirectories(String[] directories, String volumeName) {
......
for (int i = 0; i < directories.length; i++) {
processDirectory(directories[i], mClient);
}
......
}
//扫面目录的函数,里面用到了processDirectory这个native函数
//native 为java关键字,表示它将由JNI完成
private native void processFile(String path, String mimeType, MediaScannerClient client);
private static native final void native_init();
......
}
JNI 层的MediaScanner
目录frameworks/base/media/jni/android_media_MediaScanner.cpp
267 static void
268 android_media_MediaScanner_processFile(
269 JNIEnv *env, jobject thiz, jstring path,
270 jstring mimeType, jobject client)
271 {
272 ALOGV("processFile");
273
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 }
280
281 if (path == NULL) {
282 jniThrowException(env, kIllegalArgumentException, NULL);
283 return;
284 }
285
286 const char *pathStr = env->GetStringUTFChars(path, NULL);
287 if (pathStr == NULL) { // Out of memory
288 return;
289 }
290
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
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 }
问题:Java申明的函数是如何找到相应的JNI文件的?
- 静态注册
效率低用的少
- 动态注册
Java Native 函数和JNI函数一一对应,故可以通过JNINativeMethod结构记录这种关系
基本结构
typedef struct {
//java类中native函数的名字,不用携带包名,例如“native_init”
const char* name;
//java函数的签名信息,用字符串表示,是参数类型和返回值类型的组合
const char* signature;
//JNI层对应函数的函数指针,它是void* 类型 void* fnPtr;
}JNINativeMethod;
具体实例
static const JNINativeMethod gMethods[] = {
//
{
"processDirectory",
"(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
(void *)android_media_MediaScanner_processDirectory
},
//
{
"processFile",
"(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
(void *)android_media_MediaScanner_processFile
},
.......
//
{
"native_init", "()V",
(void *)android_media_MediaScanner_native_init
},
......
};
定义完毕以后需要执行注册
int register_android_media_MediaScanner(JNIEnv *env){
return AndroidRuntime::registerNativeMethods(env,
kClassMediaScanner, gMethods, NELEM(gMethods));
}
问题:什么时候执行注册呢?
在Java层执行System.loadLibrary加载JNI动态库后,紧接着会查找该库中一个JNI_OnLoad
的函数,如果有的话,动态注册就在这里完成。然而在MediaScanner对应的android_media_MediaScanner.cpp
中并没有发现这个函数。由于多媒体系统中很对地方用到JNI,所以register_android_media_MediaScanner
这个注册方法被放在了android_media_MediaPlayer.cpp
的JNI_OnLoad
方法中,当然还有其它的多媒体相关的注册函数。
Jint JNI_OnLoad(JavaVm* vm,void* reserved);
传入一个虚拟机对象,用来生产JNIEnv结构体,JNIEnv
结构体提供JNI系统操作方法。