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): jintJNI_CreateJavaVM(JavaVM **p_vm, JNIEnv **p_env, void **args); 调用这个函数就可以创建一个JavaVM,并获得一个可用的JNIEnv。用于由Native Code操作一个Java空间。 jintGetEnv(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进行注册。
函数声明如下: intjniRegisterNativeMethods(JNIEnv* env, const char*className, const JNINativeMethod* gMethods, int numMethods); 参数1是要使用的JNIEnv. 参数2是要会使用到这系列函数的Java Class, 在这个Class中会有对应每个native function的声明,就是带有native前缀的名字。 参数3和4来指定Native函数表和数量。
看一个简化的实例: intregisterWebFrame(JNIEnv* env) { jclass clazz = env->FindClass("android/webkit/BrowserFrame"); LOG_ASSERT(clazz, "Cannot find BrowserFrame");
gFrameField = env->GetFieldID(clazz, "mNativeFrame", "I");
env->DeleteLocalRef(clazz);

returnjniRegisterNativeMethods(env, "android/webkit/BrowserFrame",
gBrowserFrameNativeMethods, NELEM(gBrowserFrameNativeMethods));
} 这个表会传到jniRegisterNativeMethods中执行注册,这个函数实现在 dalvik/libnativehelper/JNIHelp.c中 。(学习资料: Dalvik虚拟机JNI方法的注册过程分析)
这部分的主要参考资料: i. Dalvik虚拟机JNI方法的注册过程分析 ii. JNI Spec from Oracle iii.JNI Tips for Android

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


更多相关文章

  1. android 输入法框梳理
  2. android.app.Activity 的介绍(转)
  3. Android(安卓)Adapter
  4. WebView的Java和javascript相互调用
  5. android audio系统的概况
  6. (Linux平台)在Android中调用JNI
  7. android中View的实时刷新
  8. Android(安卓)Studio adb无法启动解决方案
  9. Handler,MessageQueue,Looper,你所不知道的Asynchronous

随机推荐

  1. 转行Android后第一次面试某鹅被坑,那些一
  2. Android中图片实现按钮点击效果
  3. android:layout_marginLeft指该控件距离
  4. 对Android初学者学习中的几点建议
  5. Transformer Prime 变形平板:10 寸 Super
  6. 关于Android进程知识,你需要知道这些(一)
  7. Android(安卓)企业微信登录 接入指南
  8. Android(安卓)Q 适配详细操作
  9. Android(安卓)中如何将带有html格式的文
  10. 安卓端app开发!连续四年百度Android岗必问