• [Android Webkit]JNI基础及Java层与C++层的交互


    1. JNI 注册


    1.1. JNI的基础结构 


         JAVA == JNI == Native Code

         JNI(Java Native Interface)是Java与Native Code(C/C++/...)代码交互的中介,Java+JNI构成主程序, JNI+Native Code以动态库的形式供程序调用。

         JNI的实现可涉及两个关键类:JNIEnv和JavaVM。两者都可以理解为函数表(Function Pointer Table), 前者是使用Java程序创建的运行环境(从属于一个JVM,即前者)提供JNI Native函数。(学习资料:  Android JNI若干问题总结):

            JNIEnv称为JNI Interface Pointer, 是提供JNI Native函数的基础环境,线程相关,不同线程的JNIEnv相互独立。JavaVM则可以在进程中的各线程间共享。理论上一个进程可以有多个JavaVM,但Android只允许一个(  JavaVM and JNIEnv)。

        使用JavaVM可以获取JNIEnv, 下面两个函数(这是C函数,C++的调用稍有差异, 参考 Invocation APIs):
               jint JNI_CreateJavaVM(JavaVM **p_vm, JNIEnv **p_env, void **args); 
                   调用这个函数就可以创建一个JavaVM,并获得一个可用的JNIEnv。用于由Native Code操作一个Java空间。
               jint GetEnv(JavaVM *vm, void **env, jint version);
                   从一个已存在的JavaVM(vm)中获取一个可用的JNIEnv。version用于指定请求的JNI的版本。

    1.2 如何实现

      一个基本流程如下:
        i. Java程序加载一个Native Library(动态库)
        ii. 如果库实现了JNI_OnLoad,就调用它进行初始化。
        iii. 调用时,如果程序已经注册了Native Functions或者有一个依据调用的Java类命名的native函数,则调用这个函数。
        iV. 结束时,如果库实现了JNI_OnUnload,就调用它进行一些清理操作。

       对于使用JNIEnv初始化可以分为两种形式,  一种是简单的把JNI的代码与Java使用特定的声明形式,另一种则是使用Native Library注册的形式。

         第一种是最基本的形式,不需要提供额外的函数,只是要求作为JNI接口的函数定义必须以调它的Java类的名称开始,并声明为JNIEXPORT, 比如:
            .Java类:com.example.test.MainActivity要使用一个Native函数 int GetSum(int a,int b).
            .对应Native函数的定义就是JNIEXPORT jint JNICALL Java_com_example_test_MainActivity_GetSum(JNIEnv * env, jclass obj, jint a, jint b);
              其中JNIEnv是一个接口指针,供Native Code访问Java空间。 jclass obj则是代表了调用者的this指针。

         它的使用方法是在特定的Java类中调用Sytem.loadLibrary加载库就可以使用了。
         参考文档:  JNI Spec from Oracle

        第二种则比较灵活。目的是在Native Code library时动态地注册JNI函数,这样更易于变化。
        基本步骤是:
          1. Java程序使用System.loadLibrary或System.load加载某个native library.
          2. Native Library实现一个约定的JNI_OnLoad函数,并在其中注册Native Functions。
          3. 在Library中实现一个JNI_OnUnload函数做一些收尾操作。
            参考文档:  Native Libraries

       其中JNI_OnLoad的定义为: JNIEXPORT jint JNI_OnLoad(JavaVM * vm, void* reserved);
       第一个参数是JavaVM对象,所以还需要先获取到JNIEnv对象。基本的执行流程如下:
             1. 调用JavaVM的GetEnv方法,获取可用的JNIEnv对象
             2. 调用JNIEnv的RegisterNatives方法或者C接口jniRegisterNativeMethods来注册Native functions.
             3. 返回Native Functions所支持的JNI版本。详细的版本说明见 JNI Spec.

    时序图如下(来源: Dalvik虚拟机JNI方法的注册过程分析):

    1.3 WebCore的实现

    WebCore使用了Native Library实现方法,实现了一个JNI_OnLoad来实现注册操作。还有一个重要特征是我们在Android下是基于Dalvik虚拟机,与JVM会有所不同。简述其过程如下
    (/external/webkit/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp):
     1. 注册函数列表gWebCoreRegMethods,里包含了多个对象的不同注册方法,比如:
       static RegistrationMethod gWebCoreRegMethods[] = {
         { "JavaBridge", android::registerJavaBridge },
         { "JniUtil", android::registerJniUtil },
         { "WebFrame", android::registerWebFrame },
         { "WebCoreResourceLoader", android::registerResourceLoader },
         { "WebViewCore", android::registerWebViewCore },
         
         ......
         };

      2. 在JNI_OnLoad里执行gWebCoreRegMethods中的每个注册函数。

      3. 在每个注册函数中,又有一个导出的native functions列表,比如
    (/external/webkit/Source/WebKit/android/jni/WebCoreFrameBridge.cpp):
        static JNINativeMethod gBrowserFrameNativeMethods[] = {
         /* name, signature, funcPtr */
           { "nativeCallPolicyFunction", "(II)V",  (void*)CallPolicyFunction},
           { "nativeLoadUrl", "(Ljava/lang/String;Ljava/util/Map;)V",    (void*)LoadUrl},
            ......
         };
        *注意导出的函数名有native前缀。


       4. 调用jniRegisterNativeMethods进行注册。

       函数声明如下:
         int jniRegisterNativeMethods(JNIEnv* env, const char* className,  const JNINativeMethod* gMethods, int numMethods);
          参数1是要使用的JNIEnv.
          参数2是要会使用到这系列函数的Java Class, 在这个Class中会有对应每个native function的声明,就是带有native前缀的名字。
          参数3和4来指定Native函数表和数量。

       看一个简化的实例:
        int registerWebFrame(JNIEnv* env)
        {
           jclass clazz = env->FindClass("android/webkit/BrowserFrame");
          LOG_ASSERT(clazz, "Cannot find BrowserFrame");
          gFrameField = env->GetFieldID(clazz, "mNativeFrame", "I");
          env->DeleteLocalRef(clazz);
    
          return jniRegisterNativeMethods(env, "android/webkit/BrowserFrame",
                   gBrowserFrameNativeMethods, NELEM(gBrowserFrameNativeMethods)); 
       }
       这个表会传到jniRegisterNativeMethods中执行注册,这个函数实现在 dalvik/libnativehelper/JNIHelp.c中 。(学习资料: Dalvik虚拟机JNI方法的注册过程分析)

    这部分的主要参考资料:

    2. Java/C++层通讯


    两者通讯的模式,以EventHub为中心,以消息传递方式进行交互。

    下图是加载页面的时序图:


    实例化WebView
    流程如下:
    • 创建CallbackProxy对象
    • 创建WebViewCore对象
      1. 调用System.loadLibrary载入webcore相关类库(C层)
      2. 如果是第一次初始化WebViewCore对象,创建WebCoreTherad线程
      3. 创建EventHub对象,处理WebViewCore事件
      4. 获取WebIconDatabase对象实例
      5. 向WebCoreThread发送初始化消息
        • 创建BrowserFrame对象
        • 向WebView发送WEBCORE_INTIALIZED_MSG_ID消息,通知初始化完成
    • 获取WebViewDatabase实例
    • 调用init初始化WebView
    • 收到WEBCORE_INITIALIZED_MSG_ID消息后,调用nativeCreate

    转载请注明出处:   http://blog.csdn.net/horkychen

    参考: WebKit for Android分析


  • 相关阅读:
    javamail发送邮件
    java复制文件夹中的所有文件和文件夹到另一个文件夹中
    jsp中使用out和response.getOutputStream的方法
    PHP数组用法
    PHP中的session
    java中list按照某个属性排序方法
    java读取xml文件内容
    C#实现验证码
    java实现验证码功能
    Java IO(四)------字节输入输出流
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3299437.html
Copyright © 2020-2023  润新知