美文网首页
浅析Android-JNI

浅析Android-JNI

作者: NoOneDev | 来源:发表于2016-12-13 11:24 被阅读78次

    概述

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

    图解如下


    JNI示意图

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

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

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

    实例

    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文件的?
    • 静态注册
    静态注册流程

    效率低用的少

    • 动态注册
      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));
    }
    
    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

          本文链接:https://www.haomeiwen.com/subject/gzbvmttx.html