Android 2.3 Webkit

1.前言

大家对WebView应该不陌生, 它是Android里面用来显示网页的控件, 用它显示网页只需要几行代码, 如下:

public class WebViewDemoActivity extendsActivity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

mWebView = (WebView) findViewById(R.id.webview);

mWebView.loadUrl("http://www.2cto.com");

}

}

这样在WebViewDemoActivity启动时就会显示出google的主页, 我们还可以在里面输入关键字进行搜索。究竟WebView的loadUrl函数里面究竟做了哪些事情呢?我们带着好奇心走进android的Webkit世界。

2.加载网页过程的UML序列图

这里先给出loadUrl整个过程的uml序列图, 然后再慢慢分析。。

3.WebKit介绍

前面提到了Webkit,那究竟Webkit是什么呢?它和我们所熟悉的WebView是什么关系呢?这里引用网上的解释:“WebKit是一个开源的浏览器网页排版引擎,包含WebCore排版引擎和JSCore引擎。WebCore和JSCore引擎来自于KDE项目的KHTML和KJS开源项目。Android平台的Web引擎框架采用了WebKit项目中的WebCore和JSCore部分,上层由Java语言封装,并且作为API提供给Android应用开发者,而底层使用WebKit核心库(WebCore和JSCore)进行网页排版。”可见,WebView就属于Android的Webkit提供给应用开发者用的上层API部分。
很多伟大的软件都是可移植的,Webkit也不例外。目前Webkit支持Qt,Gtk,Android等等。大家都知道移植是有条件的,比如要将linux系统移植到某个硬件平台,那么这个硬件平台至少要有个硬件定时器来进行进程调度,要有mmu来支持虚拟内存管理,另外对系统ram和flash的空间也是有一定底线要求的。移植Webkit所要求的条件是什么呢?先分析Webkit加载网页的这样一个过程:
l从网络上把url地址上的html文件下载到本地
l词法分析和语法分析
l绘制
很明显,Webkit需要依赖平台相关的网络接口和图形接口,如下图所示:
Android 2.3 Webkit_第1张图片
可移植的软件都可以分为两部分:平台相关代码和平台无关代码。这两部分之间的交互是通过固定接口来完成的,当然Webkit也不例外。对于Android Webkit,我们把它简单看为两部分:Android和WebCore。 Android到Webcore方向当然是直接调用的关系, 而WebCore有时也需要回调Android部分的代码, 这就需要通过WebCore定义的一套固定的接口来完成。如下图所示, ResourceHandle是WebCore获取网络资源的接口, Android平台对这个接口的实现是调用了Android API中的android.net.*包里面的类实现的。 GraphicContext是WebCore绘制页面的接口, Android平台对这个接口的实现是通过调用Android平台的2D绘图引擎Skia实现的。



4.加载网页过程分析

有一个很重要的概念需要解释一下,Frame,翻译成中文是“帧”,一个html页面构成如下图,即一个html页面包含一个MainFrame,MainFrame中又可以包含0到n个子Frame。一个Frame对应一个Url,在MainFrame里包含子Frame就意味着把子Frame的内容嵌入到MainFrame的一块空间里面。

在WebKit里面有个结构体WebCore::Frame,这个数据结构就包含了,一个网页解析后的全部数据。下面就是从一个Url到生成一个MainFrame的过程。
Android 2.3 Webkit_第2张图片
接下来就是要将Main frame里面的数据显示出来,这部分代码是每个平台的实现都是不同的,我们以Android为例。首先需要说明的是,Android Webkit加载网页是通过三个线程协作完成的:
lUI线程:Android图形系统派发事件的线程,WebView的onDraw函数和onTouchEvent函数就是运行在这个线程。UI线程只做一些轻量级的工作,会将耗时的工作转给Webcore线程进行处理。
lWebCore线程:在andriod.webkit.WebViewCore类中创建,负责html文件解析等任务。
l下载线程:在android.webkit.WebViewWorker中创建,负责下载从指定url下载html。

当调用WebView.loadUrl时线程协作图如下,UI线程向WebCore线程发送消息,WebCore线程接到消息会把下载任务交给下载线程,下载线程下载完毕后通知WebCore线程进行html的解析,WebCore线程解析完html后通知UI线程刷新界面。

回顾一下这个图:

Webcore线程就是通过ResourceHandleAndroid来启动下载线程下载html的,然后WebCore线程进行html解析,最后生成能够描述整个html页面的Main frame,再通知UI线程绘制页面。假设UI线程在onDraw函数里需要通过分析Mainframe来绘图,那么很可能给人不流畅的感觉。所以WebCore线程在生成Frame结构体之后,又做了一个工作,就是解析Main frame并绘制到一块内存上面,而UI线程在onDraw时只需把这块内存上的内容绘制到屏幕上即可,这块内存叫PictureSet。Picture是一个图片集合,Main frame是Frame的集合,PictureSet里面的Picture和Main frame里面得Frame都是一一对应的关系。
还记得以前提到的代码层次吗?PictureSet是属于Webkit的Andriod移植层的,而Frame是属于WebCore层的,也就是说WebCore生成Frame后任务就完成了,然后会通过固定的接口通知Android移植层,Android移植层随后分析Frame来生成PictureSet。在PictureSet生成后才会通知UI线程更新界面,WebView.onDraw中便会将PictureSet绘制到画布上。
最后需要补充说明的是,Android的Webkit移植层比其它平台相比多了jni部分,通过阅读代码你会发现WebView,WebViewCore等类在Java和C++部分都存在,它们是通过Java和c++一起实现的。

5.Android Webkit插件

5.1.本章的目的

引导读者理解Android Webkit的插件框架, 通过介绍本人研究过程阅读过并认为有价值的文章,并对Android Framework源码中的SampleBrowserPlugin里难懂的地方进行解释, 希望能够清除读者对怎样写Android Webkit插件的疑惑。 SampleBrowserPlugin 是Android Webkit插件的例程, 位于源码中的development/samples/BrowserPlugin目录下, 先看一下里面的README文件吧。

5.2.推荐先看几篇文章

lhttps://developer.mozilla.org/en/Gecko_Plugin_API_Reference:虽然firefox是gecko的引擎,但是gecko和webkit的插件同样遵循NPAPI的标准, 这篇看完后就会弄如下几个问题:
(1)什么是Webkit插件, 有什么作用
(2)NPAPI一些函数和结构的作用。

lhttp://www.2cto.com/kf/201203/123782.html:这篇文章会帮助你对Android Webkit插件有个简单的了解。

lhttp://www.2cto.com/kf/201203/123783.html:这篇文章提到了Android Webkit插件的两种模式。

5.3.架构

插件是用来扩展webkit的, 是webkit的智囊团。 例如有一种新类型的数据嵌在网页里,如下图所示, webkit不知如何处理, 此时webkit就会问他的插件们如何处理"application/x-shockwave-flash”类型的数据。


<embed src="http://player.youku.com/player.php/Type/Folder/Fid/13005645/Ob/1/Pt/0/sid/XMzAxNDEwMTY4/v.swf"
quality="high"
width="480"
height="400"
align="middle"
allowScriptAccess="always"
allowFullScreen="true"
mode="transparent"
type="application/x-shockwave-flash">
</embed>

webkit写在先,插件产生再后。Webkit的设计者写基类,插件编写者写子类来实现基类的虚函数。同时,webkit也提供api供插件的子类调用。这样解释来看,架构和Android Framework的思想一致,插件系统的控制点在webkit (framework)。
Android 2.3 Webkit_第3张图片
上图的NPPluginFuncs是插件必须要实现的接口,而如果你的插件要扩展浏览器规定的接口,向通过网页中的javascript脚本调用到插件的函数,就需要继承NPClass来做扩展。下面代码示范如何在网页中调用插件的扩展接口:
1.<embedtype="application/plugin-mimetype">
2.<script>
3. varembed=document.embeds[0];
4. embed.nativeMethod();
5. alert(embed.nativeProperty);
6. embed.nativeProperty.anotherNativeMethod();
7.</script>

5.4.编译安装SampleBrowserPlugin

l 进入android源码顶层目录运行:make SampleBrowserPlugin

会生成out/target/product/generic/data/app/SampleBrowserPlugin.apk

l 开启模拟器(模拟器创建时需要添加sdcard支持)

l 运行adb install “path to SampleBrowserPlugin.apk”

5.5.运行例程

l 写个s.html文件内容如下:

<objecttype="application/x-testbrowserplugin" height=50 width=250>

<param name="DrawingModel"value="Surface" />

<param name="PluginType"value="Background" />

</object>

l 运行adb push “path to s.html” /mnt/sdcard

l 在模拟器中启动Browser程序, 在地址栏输入:file:///mnt/sdcard/s.html或file:///sdcard/s.html,结果如下图说明浏览器正确加载运行了插件。



5.6.SampleBrowserPlugin.apk里面包含了什么

l libsampleplugin.so: NPAPI中plugin方的实现, 会安装到/data/data/com.android.sampleplugin/lib目录下。

l 一些配合libsampleplugin.so工作的java class。

l 一个“莫名其妙”的Service。

为什么说它“莫名其妙”?让我们看看SamplePlugin.java的内容:

public class SamplePluginextends Service {

@Override

public IBinder onBind(Intentintent) {

// TODO Auto-generated method stub

return null;

}

}

初看觉得写代码的人是在开玩笑吧, 这样的Service能有什么用呢? 再看这个Service在AndroidManifest.xml里的描述:

<service android:name=".SamplePlugin">

<intent-filter>

<action android:name="android.webkit.PLUGIN" />

</intent-filter>

<meta-data android:name="type" android:value="native"/>

</service>

不过没有这个Service, 现在有点启发了吗?试想一下Webkit是怎样知道这个插件的存在的呢?

Webkit是通过PackageManager查询系统中能够响应android.webkit.PLUGIN的Service来得到插件安装的路径的。

5.7.对Surface模式和Bitmap模式的理解

l Surface模式:如果plugin要绘制的内容是动态的就需要在单独的surface上绘图。

l Bitmap模式:Webkit为Plugin提供绘图的Bitmap, Plugin在上面绘图后Webkit再把Bitmap 的内容绘制到WebView所在的Surface上面。

5.8.具体分析

百闻不如一见, 去看SampleBrowserPlugin的代码吧, 结合上面提到的文档, 条理就会很清楚。

原文链接:http://www.2cto.com/kf/201203/123786.html

更多相关文章

  1. Android 在非UI线程中读网络数据
  2. Android使用AsyncTask实现多线程下载的方法
  3. Android Gradle和Gradle插件区别
  4. android studio 主线程中访问网络,遇到android.os.NetworkOnMainT
  5. Android Sqlite 数据库多线程操作
  6. cordova 插件 开发添加 android 权限
  7. [Android] 图片JNI(C++\Java)高斯模糊 多线程
  8. 【国外转】Spring Android and Maven (Maven Integration for Ec

随机推荐

  1. mysql在项目中怎么选事务隔离级别
  2. .Net Core导入千万级数据至Mysql的步骤
  3. MySQL大小写敏感的注意事项
  4. MySQL 使用事件(Events)完成计划任务
  5. MySQL触发器的使用
  6. Oracle更换为MySQL遇到的问题及解决
  7. MySQL 重命名表的操作方法及注意事项
  8. Mysql官方性能测试工具mysqlslap的使用简
  9. MySQL官方导出工具mysqlpump的使用
  10. 新手必备之MySQL msi版本下载安装图文详