浅析Android-JNI

概述

** JNI (Java Native Interface)是帮助Java调用Native库的桥梁**

图解如下


浅析Android-JNI_第1张图片
JNI示意图

JNI 是通过C/C++实现的一层桥梁。
那么Native 世界是什么呢?说简单一点就是C/C++底层世界。

为什么需要这一层呢?
以我现在的理解是,在两个不同的世界直接通讯是可行的,但是会很麻烦,因为你每一次通讯都必须讲两个世界的语言翻译一下。如果我们实现一个大家公认的翻译程序,那么到时候只需调用一下这些已经定义好的接口就行了!这就是JNI也叫Java 和 Native 之间的接口。

既然知道他是什么之后,我们要开始了解他是真么运行的!

实例

浅析Android-JNI_第2张图片
Jni_MediaScanner.png

注意:名字是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文件的?
  • 静态注册
浅析Android-JNI_第3张图片
静态注册流程

效率低用的少

  • 动态注册
    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));
}
浅析Android-JNI_第4张图片
JNI动态注册
问题:什么时候执行注册呢?

在Java层执行System.loadLibrary加载JNI动态库后,紧接着会查找该库中一个JNI_OnLoad的函数,如果有的话,动态注册就在这里完成。然而在MediaScanner对应的android_media_MediaScanner.cpp中并没有发现这个函数。由于多媒体系统中很对地方用到JNI,所以register_android_media_MediaScanner这个注册方法被放在了android_media_MediaPlayer.cppJNI_OnLoad方法中,当然还有其它的多媒体相关的注册函数。

Jint JNI_OnLoad(JavaVm* vm,void* reserved);

传入一个虚拟机对象,用来生产JNIEnv结构体,JNIEnv结构体提供JNI系统操作方法。

参考:

http://www.jianshu.com/p/1e7e6689e5b1

你可能感兴趣的:(浅析Android-JNI)