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分析

[Android Webkit]JNI基础及Java层与C++层的交互的更多相关文章

  1. [Android]-图片JNI(C++\Java)高斯模糊的实现与比較

    版权声明:本文作者:Qiujuer https://github.com/qiujuer; 转载请注明出处,盗版必究! !! https://blog.csdn.net/qiujuer/article ...

  2. Android使用JNI(从java调用本地函数)

    当编写一个混合有本地C代码和Java的应用程序时,需要使用Java本地接口(JNI)作为连接桥梁.JNI作为一个软件层和API,允许使用本地代码调用Java对象的方法,同时也允许在Java方法中调用本 ...

  3. Android NDK开发之从Java与C互调中详解JNI使用(一)

    生活 这一个礼拜过得真的是苦不堪言,上周因为打球脚踝直接扭伤,肿的想猪蹄一样,然后休息几天消肿了,可以缓慢龟速的行走了,然而五一回来上班第一天,上班鞋子还能穿上,下班脚已插不进鞋子里面了,好吧,又肿回 ...

  4. Android webkit 事件传递流程详解

    前言:基于android webview 上定制自己使用的可移植浏览器apk,遇到好多按键处理的问题.所以索性研究了一下keyevent 事件的传递流程. frameworks 层 keyevent ...

  5. Android webkit 事件传递流程通道分析

    前言:基于android webview 上定制自己使用的可移植浏览器apk,遇到好多按键处理的问题.所以索性研究了一下keyevent 事件的传递流程. frameworks 层 keyevent ...

  6. Android webkit keyevent 事件传递过程

    前言:基于android webview 上定制自己使用的可移植浏览器apk,遇到好多按键处理的问题.所以索性研究了一下keyevent 事件的传递流程. frameworks 层 keyevent ...

  7. Android 中JNI创建实例

    参考文档: http://blog.sina.com.cn/s/blog_a11f64590101924l.html http://www.cnblogs.com/hoys/archive/2010/ ...

  8. [ 转载 ] Android JNI(一)——NDK与JNI基础

    Android JNI(一)——NDK与JNI基础 隔壁老李头 关注  4.4 2018.05.09 17:15* 字数 5481 阅读 11468评论 8喜欢 140 本系列文章如下: Androi ...

  9. Android JNI(一)——NDK与JNI基础

    本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...

随机推荐

  1. (Error) The type AESKeyGenerator is not accessible due to restriction on required library.

    error for 'Access restriction: The type AESKeyGenerator is not accessible due to restriction on requ ...

  2. Python算术运算符

    Python 运算符 什么是运算符? 本章节主要说明Python的运算符.举个简单的例子 4 +5 = 9 . 例子中,4和5被称为操作数,"+"号为运算符. Python语言支持 ...

  3. 自定义控件出现“loaded nib but the view outlet was not set”

    我出现这个错误是因为我的自定义控件的名字和项目中一个控制器的名字很像 控制器 DDGuessYourLikeViewController 自定义控件 DDGuessYourLikeView 默认的, ...

  4. AFN发送请求失败

    发送请求出现这个错误 Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Coc ...

  5. ASP.NET菜鸟之路之Request小例子

    背景 我是一个ASP.NET菜鸟,暂时开始学习ASP.NET,在此记录下我个人敲的代码,没有多少参考价值,请看到的盆友们为我点个赞支持我一下,多谢了. Request获取值 Request获取值有两种 ...

  6. Delphi 动态创建组件,单个创建、单个销毁

    效果图如下: 实现部分代码如下: var rec: Integer = 0; //记录增行按钮点击次数 implementation {$R *.dfm} //动态释放单个组件内存,即销毁组件 pro ...

  7. Ajax的load方法演示

    load方法的参数形式为: load(url,[data],[callback]); 其中url为请求HTML页面的URL地址.[data]表示发送至服务器的key/value数据.callback表 ...

  8. A题

    A - A Time Limit:1000MS     Memory Limit:131072KB     64bit IO Format:%I64d & %I64u   Descriptio ...

  9. .NET MVC插件化开发框架源码(插件功能完善版)

    离上次第一次上传源码一个多星期了,在工作之余今天终于把插件管理部门的功能全部完善了,已可用于实际开发,管理界面因为没人帮忙设计,所以有点丑,今天这版算是0.1.0.0吧,后面我会发布很多插件来填充这个 ...

  10. 之前C#代码的重新设计

    /* 我用python重构了一把这个代码 大家的反应似乎是过度设计了 好吧,我决定不那么激进,采用更中庸一些的重构 我也有些疑惑: 是否如果重构后的代码比重构前要多,就算过度了呢? */ void m ...