Android平台webkit组件分析

转自ITeye: constGU

本文将围绕android平台webkit组件中,java层的几个主要类进行分析,说明各个类的职责以及类之间的关系。若有错误疏漏之处,望不吝指出。
(源码所在目录  ${framework_sourcecode_loc}\core\java\android\webkit )

一、概述

Android平台webkit组件java层封装了一系列浏览网页相关的功能,对开发者而言,主要功能有以下两点:

1、接收上层请求(eg:loadUrl, goBack, reload...),并将请求委托给底层模块(so库)处理
2、将处理的中间过程和结果通过回调接口通知上层(eg:onPageStarted, onPageFinished, shouldOverrideUrlLoading...)

 

二、主要功能类介绍


1、类层次


自顶向下依次为:

WebView
WebViewProvider, WebViewClassic
WebViewCore, EventHub, CallbackProxy
BroswerFrame

 
(说明:层次划分的依据是类之间的创建和调用关系,不一定非常严格,可能由于业务逻辑需要,会出现底层调用上层的情况,例如:BroswerFrame会调用CallbackProxy中定义的接口)


2、类之间关系


在介绍类之间关系之前,有两点需要先说明:
a、函数根据调用发起者的层次关系,分为上层请求 和 底层回调两种。
上层请求函数包括:
    loadUrl, goBack, reload 等
底层回调函数包括:
    onPageStarted, onPageFinished, shouldOverrideUrlLoading 等

b、函数可能在UI线程执行,也可能在WebCore线程执行。
   UI线程:即创建Framework WebView组件所在线程 
   WebCore线程:native层通过jni调用java函数所在线程(即调用WebViewCore类initializeSubwindow的线程)


在主要功能类中,函数可以根据功能划分,分为3类:

    业务逻辑相关
    View显示相关
    Scroll控制相关

下面以业务逻辑相关的函数为例,说明各个类之间的关系:(蓝色表示调用执行在UI线程,红色表示执行在WebCore线程)

     上层请求路径:(loadUrl, goBack, reload)
         WebView->WebViewClassic->WebCore->WebCore.EventHub->BrowserFrame->native
     底层回调路径:(onPageStarted, onPageFinished, shouldOverrideUrlLoading)
         native->BrowserFrame->CallbackProxy->CallbackProxy.Handler->WebViewClassic/WebViewClient/WebChromeClient     

调用关系图如下图:
(图中,蓝色箭头表示调用在UI线程执行,红色表示在WebCore线程执行,蓝色圆角矩形表示类,红色圆角矩形表示接口)
 

 

3、执行线程及线程间通信

由上图可以看出,在WebViewCore和CallbackProxy层以上,函数在UI线程执行,以下在WebCore线程执行。
对上层开发者而言,WebCore线程是透明的,不需要考虑线程同步的问题,所有对上层接口的回调事件都已经由CallbackProxy抛回UI线程处理。
线程间通信是通过Framework android.os.Handler类实现,EventHub和CallbackProxy类分别负责 UI->WebCore 和 WebCore->UI 的消息传递。

 

4、其他


CallbackProxy类其实包含两个功能:
a、对WebViewCore而言,CallbackProxy用于请求上层对某些事件做处理,是一种主动的调用请求
b、对于BroswerFrame而言,CallbackProxy用于将中间过程和结果回调上层,是一种状态的回调

看代码的过程中,总感觉这里分为两个类会更清晰一点。具体是不是违反了SRP原则,就见仁见智了。
(其实webkit java层代码基本没有真正的业务逻辑处理,业务逻辑都在native层,后续会补上对native层代码的分析)

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
android.webkit.WebView 是 Android 系统提供的一个用于显示网页内容的 View 组件。它可以加载并渲染 HTML 页面,并提供了与 JavaScript 的交互功能。 以下是一些常见的 android.webkit.WebView 的用法和功能: 1. 加载网页:使用 `loadUrl()` 方法可以加载指定 URL 的网页内容。例如:`webView.loadUrl("https://www.example.com");` 2. WebView 设置:可以使用 WebSettings 对象来配置 WebView 的各种设置,如启用 JavaScript 、启用缩放等。例如:`webView.getSettings().setJavaScriptEnabled(true);` 3. WebViewClient:可以通过设置 WebViewClient 来控制 WebView 的页面加载行为,例如处理页面加载错误、拦截 URL 请求等。可以通过继承 WebViewClient 类并重写相应方法来实现自定义的 WebView 行为。 4. WebChromeClient:可以通过设置 WebChromeClient 来处理一些与 WebView 相关的事件,如页面标题改变、JavaScript 的 alert 对话框等。可以通过继承 WebChromeClient 类并重写相应方法来实现自定义的 WebView 行为。 5. JavaScript 交互:可以通过 WebView 的 `addJavascriptInterface()` 方法将 Java 对象暴露给 JavaScript,从而实现 Java 代码和 JavaScript 代码之间的交互。 6. WebView 生命周期管理:在 Activity 或 Fragment 中,需要在合适的生命周期方法中调用 WebView 的相应方法,如 `onPause()`、`onResume()`、`onDestroy()` 等,以确保 WebView 的正确管理和释放。 请注意,在 Android 7.0 及以上版本,WebView 是以独立的 APK 形式提供的,需要根据系统的 WebView 版本进行相应的处理。 希望以上信息对您有所帮助。如果您还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值