android 2.3 webkit

来源:互联网 发布:mac比较好的杀毒软件 编辑:程序博客网 时间:2024/04/29 18:49

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.google.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需要依赖平台相关的网络接口和图形接口, 如下图所示:


    可移植的软件都可以分为两部分:平台相关代码和平台无关代码。 这两部分之间的交互是通过固定接口来完成的,当然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的过程。


       接下来就是要将Main frame里面的数据显示出来, 这部分代码是每个平台的实现都是不同的,我们以Android为例。 首先需要说明的是, Android Webkit加载网页是通过三个线程协作完成的:

l        UI线程:Android图形系统派发事件的线程, WebView的onDraw函数和onTouchEvent函数就是运行在这个线程。UI线程只做一些轻量级的工作, 会将耗时的工作转给Webcore线程进行处理。

l        WebCore线程:在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.     推荐先看几篇文章

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

l        http://blog.csdn.net/ownerwu/archive/2011/05/18/6429072.aspx:这篇文章会帮助你对Android Webkit插件有个简单的了解。

l        http://blog.csdn.net/qyqzj/archive/2010/06/14/5670816.aspx:这篇文章提到了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)。


上图的NPPluginFuncs是插件必须要实现的接口, 而如果你的插件要扩展浏览器规定的接口, 向通过网页中的javascript脚本调用到插件的函数, 就需要继承NPClass来做扩展。下面代码示范如何在网页中调用插件的扩展接口:

1.                 <embed type="application/plugin-mimetype">  

2.                 <script>  

3.                   var embed = document.embeds[0];   

4.                   embed.nativeMethod();   

5.                   alert(embed.nativeProperty);   

6.                   embed.nativeProperty.anotherNativeMethod();   

7.                 </script> 

 

5.14.  编译安装SampleBrowserPlugin

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

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

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

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

5.15.  运行例程

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.16. SampleBrowserPlugin.apk里面包含了什么

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

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

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

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

public class SamplePluginextendsService {

    @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.17.  对Surface模式和Bitmap模式的理解

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

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

5.18. 具体分析

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

原创粉丝点击