android 浏览器

Android 4.0 Browser增加了表单自动填充功能,比较了一下UC、QQ、Opera、海豚浏览器等,都没有提供此项功能。问了很多人,也没人用过,所以就上网收集了相关资料。

先看看维基百科关于autofill词条的解释吧:

Autofill is a function in some computer applications or programs, typically those containing forms, which fills in a field automatically.

Most of the time, such as in Internet Explorer and Google Toolbar, the entries depend on the form field’s name, so as to not enter street names in a last name field or vice-versa. For this use, RFC 3106 proposed names for such form fields, but in the HTML 5 specification this RFC is no longer referenced, thus leaving the selection of names up to each browser’s implementation.

Chrome浏览器对于自动填充的说明:

您是否厌倦了反复填写信息相同的网络表单?您可以使用自动填充功能,只需点击一下便可完成表单填写。

如果需要详细了解自动填充的来龙去脉,可以阅读RFC 3106。简单说,自动填充就是为了提升用户体验整出来的东西,有了它,你就不用每次注册帐号时无聊的填写姓名、地址、城市等资料了。chrome浏览器做的更绝,信用卡卡号都可以自动填充。

Android浏览器的自动填充条目有:Full name、Company name、Address、Zip code、Country、Phone、Email等。Browser中有一个AutoFillProfileDatabase类来处理其存放,具体说就是保存在autofill.db数据库中。而WebKit的WebSettings.AutoFillProfile类就是用来表示这些字段信息的。整个处理逻辑也不复杂。

一切看起来很美妙,但要真正用起来还有许多障碍需要克服:

1、因为涉及到隐私,需要在浏览器的设置中开启Form Auto-fill选项;

2、用户需要比较勤快,事先要将上面的个人信息录入;

3、需要网站的支持,这些信息如何和表单上的字段对应,这就是RFC 3106所定义的,表单控件的命名需要遵守规范。RFC 3106给出了一个例子:

<HTML>
<HEAD>
<title> eCom Fields Example </title>
</HEAD>
<BODY>
 <FORM action="http://ecom.example.com" method="POST">
Please enter card information:
<p>Your name on the card   <INPUT type="text" name="Ecom_Payment_Card_Name" SIZE=40>
<br>The card number
  <INPUT type="text" name="Ecom_Payment_Card_Number" SIZE=19>
<br>Expiration date (MM YY)
  <INPUT type="text" name="Ecom_Payment_Card_ExpDate_Month" SIZE=2>
  <INPUT type="text" name="Ecom_Payment_Card_ExpDate_Year" SIZE=4>
  <INPUT type="hidden" name="Ecom_Payment_Card_Protocol">
  <INPUT type="hidden" name="Ecom_SchemaVersion"           value="http://www.ecml.org/version/1.1">
<br>   <INPUT type="submit" value="submit"> <INPUT type="reset">
 </FORM>
</BODY>  </HTML>

非常遗憾的是,国内的网站,包括京东、亚马逊中国、当当、淘宝等均不支持。

4、用户敢用么?某些网站可能会试图捕获隐藏字段或难以发现的字段中的信息,因此,请勿在您不信任的网站上使用自动填充功能。

5、某些网站会阻止浏览器保存您输入的内容,因此,无法在这些网站上填写表单。

总结:

1、自动填充功能基本上是无用的,至少在国内的环境下。

分类: Android浏览器 标签: android, webkit

WebKit for Android分析:表单控件

2012年4月14日 alex 没有评论

所谓表单控件,指的是html中的radio, checkbox, text, button, select等。在进行WebKit移植时,需要解决两件事:渲染和事件处理。下面就分析WebKit for Android渲染和事件处理。

一、表单控件的绘制

表单控件的渲染并不难,不外乎就是绘图和贴图。但在WebKit中,为了达到和平台一致的视感(look & feel), 定义了一个RenderTheme,包含了paintButton, paintCheckbox, paintTextField等方法,具体实现则由各平台的移植层实现。Android平台的RenderTheme类实现为RenderThemeAndroid,具体的控件绘制类位于Source/WebKit/android/下,类关系图如下:

html_control_rendering

类图中并没有TextField和TextArea的绘制类,这是因为文本框只需使用线条绘制边框及绘制文字即可,Webkit有默认的实现,Android移植中没有重新实现。

RenderSkinNinePatch类是一个实用类,用于处理图片拉伸问题。RenderSkinCombo的实现实际上是贴一个背景图片,让人感觉是一个ComboBox/ListBox,但是ComboBox/ListBox的宽高不是固定的,这样在贴图时避免不了需要对图片进行拉伸。RenderSkinNinePatch则是将图片划分为9个部分,避免拉伸带来失真。

二、表单控件的事件处理

表单控件还需要响应用户的事件,比如点击button, 选择radio button,输入文本等等。对于radio button/checkbox而言,处理比较简单,只需更新一下状态数据和图片,而ComboBox/TextField/TextArea的处理要复杂一些,因为涉及到输入。在android webkit实现中,ComboBox和文本框是调用系统控件实现的。

– 待续

分类: Android浏览器 标签: android, webkit

GDB调试webkit技巧一则

2012年3月29日 alex 2 条评论

关于如何使用gdb+gdbserver调试android webkit,请参考这篇文章。按照这种方法,每次启动gdb,都要设置调试符号查找路径,要连着输入好几个gdb命令,对于频繁调试webkit来说,很是麻烦。本文介绍的就是一种偷懒的方法。

好在GDB支持启动文件,可以将gdb命令放在一个GDB启动文件中,然后每次启动GDB时会自动加载它们。GDB的启动文件默认为.gdbinit,也可以在调用GDB时指定启动文件,例如:

gdb -command=mygdbinit x

表示要调试可执行程序x,首先从文件mygdbinit中读取命令。本文选用第二种方法,不想将项目特有的指令影响其它程序的gdb调试。很快就可以写一个启动文件和脚本来减少命令的输入:

# filename: mygdbinit

set solib-absolute-prefix /home/developer/android/out/target/product/generic/symbols/   

set solib-search-path /home/developer/android/out/target/product/generic/symbols/system/lib
target remote :5039

#!/bin/bash

./out/host/linux-x86/bin/adb forward tcp:5039 tcp:5039
./prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin/arm-eabi-gdb -command=./mygdbinit out/target/product/generic/symbols/system/bin/app_process

这个脚本在我的机器上工作得很好,但是在其他小组成员上有问题,原因在于每个人的android源码存放的位置并不相同,在mygdbinit中定义了绝对路径并不合适,简单的解决方案是每个人都把上面两个文件稍微修改一下,有没有更好的解决方法呢?

我想到的一个解决方案是定义一个环境变量$ANDROID_SOURCE,指向android源码根目录,问题也随之而来,gdb命令中并不能直接使用系统环境变量。借助于万能的互联网,最终还是找到解决方法,最终脚本如下:

# filename: mygdbinit

define loadsymbols
    shell echo set solib-absolute-prefix $ANDROID_SOURCE/out/target/product/generic/symbols/ >/tmp/tmp.webkitsymbolspath
    shell echo set solib-search-path $ANDROID_SOURCE/out/target/product/generic/symbols/system/lib >>/tmp/tmp.webkitsymbolspath
    source /tmp/tmp.webkitsymbolspath
    shell rm /tmp/tmp.webkitsymbolspath
end

loadsymbols
target remote :5039

#!/bin/bash
if [ -d $ANDROID_SOURCE ] ; then
    echo "ANDROID_SOURCE: $ANDROID_SOURCE"
else
    echo "you must define ANDROID_SOURCE environment variable to point your android source"
    exit 1
fi

$ANDROID_SOURCE/out/host/linux-x86/bin/adb forward tcp:5039 tcp:5039
$ANDROID_SOURCE/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin/arm-eabi-gdb -command=$ANDROID_SOURCE/webkit_gdbinit $ANDROID_SOURCE/out/target/product/generic/symbols/system/bin/app_process

另外在$ANDROID_SOURCE/external/webkit/Tools/gdb/下还有实用的python脚本,可用于gdb调试,打印输出webkit数据类型。

补充:gdb + ddd是一个不错的选择,可以一边调试,一边查看源代码,断点定义也比较直观,虽然比不上Visual Studio这种IDE,但调试还是可以轻松许多。

在研究了chromium for android后,发现其中有几个脚本非常有用,稍加修改后用于android webkit调试:

1、命令行上启动Browser (adb_run_browser)

#!/bin/bash

if [ $# –gt 0 ] ; then

    INTENT_ARGS=”-d $1”  # e.g. a URL

fi

adb shell am start \

  -a android.intent.action.VIEW \

  -n com.android.browser/.BrowserActivity \

  $INTENT_ARGS

2、命令行上kill掉Browser (adb_kill_browser)

#!/bin/bash

BROWSER_PID_LINES=$(adb shell ps | grep ‘ com.android.browser’)

VAL=$(echo “$BROWSER_PID_LINES” | wc –l)

if [ $VAL –lt 1 ] ; then

  echo “Not running Browser.”

else

  BROWSER_PID=$(echo $BROWSER_PID_LINES | awk ‘{print $2}’)

  if [ “$BROWSER_PID” != "" ] ; then

    set –x

    adb shell kill $BROWSER_PID

    set -

  else

    echo “Browser does not appear to be running.”

  fi

fi

分类: Android浏览器 标签: android, gdb, webkit

Android中的硬件加速

2012年2月17日 alex 没有评论

本文的主要内容来自SDK文章的"Hardware Acceleration”.

从Android 3.0开始,Android的2D渲染管线可以更好的支持硬件加速。硬件加速使用GPU进行View上的绘制操作。

硬件加速可以在一下四个级别开启或关闭:

  • Application
  • Activity
  • Window
  • View

Application级别

往您的应用程序AndroidManifest.xml文件为application标签添加如下的属性即可为整个应用程序开启硬件加速:

<application android:hardwareAccelerated="true" ...>

Activity级别

您还可以控制每个activity是否开启硬件加速,只需在activity元素中添加android:hardwareAccelerated属性即可办到。比如下面的例子,在application级别开启硬件加速,但在某个activity上关闭硬件加速。

<application android:hardwareAccelerated="true">    <activity ... />    <activity android:hardwareAccelerated="false" /></application>

Window级别

如果您需要更小粒度的控制,可以使用如下代码开启某个window的硬件加速:

getWindow().setFlags(    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

注:目前还不能在window级别关闭硬件加速。

View级别

您可以在运行时用以下的代码关闭单个view的硬件加速:

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

注:您不能在view级别开启硬件加速

为什么需要这么多级别的控制?

很明显,硬件加速能够带来性能提升,android为什么要弄出这么多级别的控制,而不是默认就是全部硬件加速呢?原因是并非所有的2D绘图操作支持硬件加速,如果您的程序中使用了自定义视图或者绘图调用,程序可能会工作不正常。如果您的程序中只是用了标准的视图和Drawable,放心大胆的开启硬件加速吧!具体是哪些绘图操作不支持硬件加速呢?以下是已知不支持硬件加速的绘图操作:

Android的绘制模型

开启硬件加速后,Android框架将采用新的绘制模型。基于软件的绘制模型和基于硬件的绘制模型有和不同呢?

基于软件的绘制模型

在软件绘制模型下,视图按照如下两个步骤绘制:

1. Invalidate the hierarchy(注:hierarchy怎么翻译?)

2. Draw the hierarchy

应用程序调用invalidate()更新UI的某一部分,失效(invalidation)消息将会在整个视图层中传递,计算每个需要重绘的区域(即脏区域)。然后Android系统将会重绘所有和脏区域有交集的view。很明显,这种绘图模式存在缺点:

1. 每个绘制操作中会执行不必要的代码。比如如果应用程序调用invalidate()重绘button,而button又位于另一个view之上,即使该view没有变化,也会进行重绘。

2. 可能会掩盖一些应用程序的bug。因为android系统会重绘与脏区域有交集的view,所以view的内容可能会在没有调用invalidate()的情况下重绘。这可能会导致一个view依赖于其它view的失效才得到正确的行为。

基于硬件的绘制模型

Android系统仍然使用invalidate()和draw()来绘制view,但在处理绘制上有所不同。Android系统记录绘制命令到显示列表,而不是立即执行绘制命令。另一个优化就是Android系统只需记录和更新标记为脏(通过invalidate())的view。新的绘制模型包含三个步骤:

1. Invalidate the hierarchy

2. 记录和更新显示列表

3. 绘制显示列表

分类: Android浏览器 标签: android

WebKit的JavaScript对象扩展

2012年1月12日 alex 7 条评论

本文的内容主要参考网上收集的资料,不过在Android 4.0 webkit上做扩展时,碰到一些问题,觉得有必要记录下来。

所谓扩展JavaScript对象,就是增加一个JS对象,但它并没有定义在标准的JS对象集合中。如果网页中包含了扩展的JS对象,使用普通的浏览器就会报JS错误。

下面以添加HelloObject对象为例说明具体步骤,该对象具有description属性:

1. 添加HelloObject.h, HelloObject.cpp, HelloObject.idl文件,简单起见,将这三个文件放到Source/WebCore/page目录下。

#ifndef HelloObject_h
#define HelloObject_h

#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>

#include "PlatformString.h"

namespace WebCore {

    class HelloObject : public RefCounted<HelloObject> {
    public:
        static PassRefPtr<HelloObject> create() { return adoptRef(new HelloObject()); }

        String description() const;
    private:
        HelloObject();
    };

} // namespace WebCore

#endif // HelloObject_h

HelloObject.h

#include "config.h"
#include "HelloObject.h"

namespace WebCore {

HelloObject::HelloObject()
{
}

String HelloObject::description() const
{
    return "Hello Object";
}

} // namespace WebCore

HelloObject.cpp

module window {

    interface [OmitConstructor] HelloObject {
        readonly attribute DOMString description;
    };

}

HelloObject.idl

2. 修改Source/WebCore/page/下的DOMWindow.h文件,添加如下声明:

class HelloObject;

public:

    HelloObject* helloObject() const;

    HelloObject* optionalHelloObject() const { return m_helloObject.get(); }

private:

    mutable RefPtr<HelloObject> m_helloObject;

3. 修改Source/WebCore/page/下的DOMWindow.cpp文件,添加接口实现:

HelloObject* DOMWindow::helloObject() const
{
    if (!m_helloObject)
        m_helloObject = HelloObject::create();
    return m_helloObject.get();
}

在DOMWindow::clear()函数中添加一行语句:

m_helloObject = 0;

4. 修改DOMWindow.idl文件,添加:

attribute [Replaceable] HelloObject helloObject;

5. 接下来需要修改编译系统,让android编译系统编译新增的文件:

首先修改Source/WebCore/Android.mk,增加page/HelloObject.cpp到LOCAL_SRC_FILES变量,其次需要修改Source/WebCore/Android.derived.v8bindings.mk,增加$(intermediates)/bindings/V8HelloObject.h到GEN变量。(注:这个是必须的,否则就不会根据HelloObject.idl生成V8HelloObject.h文件,在编译时会出错,这也是折腾了半天得出的成果)

至此,工作基本上完成,待webkit重新编译后,可以用如下的网页进行验证:

<html>
<body>
<script type="text/javascript">
document.write("<p> This is from HelloObject: ");
document.write(helloObject.description + "</p>");
</script>
</body>
</html>

 

[参考资料]

1. webkit的js对象扩展(一)——binding方式创建自定义对象(单实例)

2. android 上 webkit js 本地扩展之全局本地对象实现步骤

Webkit for Android分析

2012年1月9日 alex 5 条评论

本文是在他人文章上针对android 4.0做了一些调整和补充,所有权归原作者。原文作者信息:

WebSite: http://www.jjos.org/
作者: 姜江 linuxemacs@gmail.com
QQ: 457283

网上有许多webkit的分析文章,其中针对android porting的一篇文章WebKit – WebKit For Android,写的非常好,分析得非常深入。不过这篇文章针对的Android版本比较老(具体版本无从考究),因此本文将在这篇文章的基础上,加入android 4.0 webkit porting的一些内容。

一、Android WebKit简介

Webkit是一个开源的浏览器排版和渲染引擎,包含WebCore和JavascriptCore。WebKit有众多的实现(Qt、Gtk, windows, chromium, android, etc)。Android 4.0平台的Web引擎框架采用了WebKit中的WebCore,javascript引擎则是采用google的V8引擎。Android 4.0的webkit采用了和chromium 12.0.742.130中webkit相同的codebase,webkit版本为534.30。

二、Android WebKit模块框架

Android平台的WebKit上层由Java语言封装,并且作为API提供给Android应用开发者,而底层使用WebKit核心库(WebCore)进行网页排版。WebKit模块分为两个部分: Java层和C层(webkit库)。Java层和C层通过JNI相互调用,如图1所示:

AndroidWebKitArchitecture

图1 Android WebKit模块框架

在webkit其它平台的移植中,webkit层就是封装WebCore,为上层应用提供接口的。Android的平台具有一定的特殊性,需要提供Java API接口,应用程序框架也是基于Java的,所以在Android的移植中,webkit层实际上被拆成两部分,Java部分和C++部分,它们之间通过JNI接口进行通讯。JNI是一种双向通讯机制,Java代码可以调用C/C++代码,C/C++代码也可以调用Java代码。

通常,WebCore中回调Java的代码都位于WebKit(Android Implementation)层,但有一个例外,就是Source/WebCore/platform/android/GeolocationServiceBridge.cpp,该文件也包含回调到Java的代码。

2.1 Java层框架

2.1.1 Java层源码说明

Java层的代码位于frameworks/base/core/java/android/webkit目录下。各文件的简单说明如下:

AccessibilityInjector.java 为WebView注入Accessibility
BrowserFrame.java 对WebCore中Frame对象的Java层封装,用于创建WebCore中定义的Frame,以及为该Frame对象提供Java层回调方法
ByteArrayBuilder.java 辅助对象,用于byte块链表的处理
CacheLoader.java android 4.0 WebKit中不再使用
CacheManager.java Cache管理对象,负责Java层Cache对象管理
CallbackProxy.java 该对象是用于处理WebCore与UI线程消息的代理类。当有Web事件产生时WebCore线程会调用该回调代理类,代理类会通过消息的方式通知UI线程,并且调用设置的客户对象的回调函数。
CertTool.java 证书工具
ClientCertRequestHandler.java 处理客户端证书请求
ConsoleMessage.java 来自WebCore的Javascript控制台消息
ContentLoader.java android 4.0 WebKit中不再使用
CookieManager.java 根据RFC2109规范,管理cookies
CookieSyncManager.java Cookies同步管理对象,该对象负责同步RAM和Flash之间的Cookies数据。实际的物理数据操作在基类WebSyncManager中完成。
DataLoader.java android 4.0 WebKit中不再使用
DateSorter.java 日期排序
DebugFlags.java 定义调试标志
DeviceMotionAndroidOrientationManager.java 用于实现DeviceMotion和DeviceOrientation
DeviceMotionService.java 实现SensorEventListener接口,处理动作
DeviceOrientationService.java 实现SensorEventListener接口,处理方向变化
DownloadLister.java 下载侦听器接口
FileLoader.java android 4.0 WebKit中不再使用
FindActionModeCallback.java
FrameLoader.java Frame载入器,用于载入网页Frame数据
GeolocationPermission.java 用于管理浏览器UI的位置信息权限
GeolocationService.java 实现java侧的GeolocationServiceAndroid
HTML5Audio.java HTML5 audio支持类
HTML5VideoFullScreen.java 全屏视频视图,仅提供给浏览器使用
HTML5VideoInline.java 内嵌视频视图,仅提供给浏览器使用
HTML5VideoView.java 视频视图,仅提供给浏览器使用
HTML5VideoViewProxy.java HTML5视频视图代理类
HttpAuthHandler.java HTTP认证请求,需要用户处理
HttpAuthHandlerImpl.java HttpAuthHandler实现,仅用于Android Java HTTP stack
JniUtil.java 供JNI使用的实用类,用于获取cache目录等C代码无法直接获取的信息,以及读取资源包中的文件等
JsPromptResult.java Js结果提示对象,用于向用户提示Javascript运行结果。
JsResult.java Js结果对象,用于用户交互
JWebCoreJavaBridge.java 用Java与WebCore库中Timer和Cookies对象交互的桥接代码。
KeyStoreHandler.java https相关处理
L10nUtils.java 字符串国际化,在使用chrome http stack时用到
LoadListener.java 载入器侦听器,用于处理载入器侦听消息。android 4.0 WebKit中不再使用
MimeTypeMap.java MIME类型映射
MockGeolocation.java 模拟地理位置信息
Network.java 该对象封装网络连接逻辑,为调用者提供更为高级的网络连接接口。
OverScrollGlow.java 用于实现OverScroller效果
PerfChecker.java 性能测试
Plugin.java 插件处理相关
PluginData.java 插件处理相关
PluginFullScreenHolder.java 插件处理相关
PluginList.java 插件处理相关
PluginManager.java 插件处理相关
PluginStub.java 插件处理相关
SearchBox.java 定义搜索对话框接口
SearchBoxImpl.java 搜索对话框接口实现
SelectActionModeCallback.java
SslCertLookupTable.java https相关处理
SslClientCertLookupTable.java https相关处理
SslErrorHandler.java https相关处理
SslErrorHandlerImpl.java https相关处理
StreamLoader.java android 4.0 WebKit中不再使用
UrlInterceptHandler.java 用于google gears,已废弃
UrlInterceptRegistry.java 用于google gears,已废弃
URLUtil.java URL处理实用类
ValueCallback.java 回调接口,用于异步返回数据值
ViewManager.java 子视图管理类,主要用于管理插件视图
ViewStateSerializer.java WebView视图序列化和反序列化
WebBackForwardList.java 该对象包含WebView对象中显示的历史数据。
WebBackForwardListClient.java 浏览历史处理的客户接口类,所有需要接收浏览历史改变的类都需要实现该接口。
WebChromeClient.java Chrome客户基类,Chrome客户对象在浏览器文档标题、进度条、图标改变时候会得到通知。
WebHistoryItem.java 该对象用于保存一条网页历史数据
WebIconDatabase.java 图标数据库管理对象,所有的WebView均请求相同的图标数据库对象
WebResourceResponse.java 封装某个资源的响应信息
WebSettings.java WebView的管理设置数据,该对象数据是通过JNI接口从底层获取。
WebStorage.java 处理webstorage数据库
WebSyncManager.java 数据同步对象,用于RAM数据和FLASH数据的同步操作。
WebTextView.java 在html文本输入控件激活时,显示系统原生编辑组件
WebView.java Web视图对象,用于基本的网页数据载入、显示等UI操作。
WebViewClient.java Web视图客户对象,在Web视图中有事件产生时,该对象可以获得通知。
WebViewCore.java 该对象对WebCore库进行了封装,将UI线程中的数据请求发送给WebCore处理,并且通过CallbackProxy的方式,通过消息通知UI线程数据处理的结果。
WebViewDatabase.java 该对象使用SQLiteDatabase为WebCore模块提供数据存取操作。
WebViewFragment.java 实现WebView嵌入到Fragment中
WebViewWorker.java 实现html5 workers,在UI线程和webkit线程开启单独的线程
ZoomControlBase.java 缩放控件接口
ZoomControlEmbedded.java 内置缩放控件
ZoomControlExternal.java 扩展缩放控件,已废弃
ZoomManager.java 维护WebView的缩放状态
2.1.2 Java层主要类关系图

WebKit Java层包含79个Java文件,主要的类关系图如下:

Java layer class diagram

1)WebView

WebView类是WebKit模块Java层的视图类,所有需要使用Web浏览功能的Android应用程序都要创建该视图对象显示和处理请求的网络资源。目前,WebKit模块支持HTTP、HTTPS、FTP以及javascript请求。WebView作为应用程序的UI接口,为用户提供了一系列的网页浏览、用户交互接口,客户程序通过这些接口访问WebKit核心代码。

2)WebViewDatabase

WebViewDatabase是WebKit模块中针对SQLiteDatabase对象的封装,用于存储和获取运行时浏览器保存的缓冲数据、历史访问数据、浏览器配置数据等。该对象是一个单实例对象,通过getInstance方法获取WebViewDatabase的实例。WebViewDatabase是WebKit模块中的内部对象,仅供WebKit框架内部使用。

3)WebViewCore

WebViewCore类是Java层与C层WebKit核心库的交互类,客户程序调用WebView的网页浏览相关操作会转发给BrowserFrame对象。当WebKit核心库完成实际的数据分析和处理后会回调WebViweCore中定义的一系列JNI接口,这些接口会通过CallbackProxy将相关事件通知相应的UI对象。

4)CallbackProxy

CallbackProxy是一个代理类,用于UI线程和WebCore线程交互。该类定义了一系列与用户相关的通知方法,当WebCore完成相应的数据处理,则会调用CallbackProxy类中对应的方法,这些方法通过消息方式间接调用相应处理对象的处理方法。

5)BrowserFrame

BrowserFrame类负责URL资源的载入、访问历史的维护、数据缓存等操作,该类会通过JNI接口直接与WebKit C层库交互。

6)JWebCoreJavaBridge

该类为Java层WebKit代码提供与C层WebKit核心部分的Timer和Cookies操作相关的方法。

7)WebSettings

该对象描述了WEB浏览器访问相关的用户配置信息。

8)DownloadListener

下载侦听接口,如果客户代码实现该接口,则在下载开始、失败、挂起、完成等情况下,DownloadManagerCore对象会调用客户代码中实现的DwonloadListener方法。

9)WebBackForwardList

WebBackForwarList对象维护着用户访问历史记录,该类为客户程序提供操作访问浏览器历史数据的相关方法。

10)WebViewClient

WebViewClient类定义了一系列事件方法,如果Android应用程序设置了WebViewClient派生对象,则在页面载入、资源载入、页面访问错误等情况发生时,该派生对象的相应方法会被调用。

11)WebBackForwardListClient

WebBackForwardListClient对象定义了对访问历史操作时可能产生的事件接口,当用户实现了该接口,则在操作访问历史时(访问历史移除、访问历史清空等)用户会得到通知。

12)WebChromeClient

WebChromeClient类定义了与浏览窗口修饰相关的事件。例如接收到Title、接收到Icon、进度变化时,WebChromeClient的相应方法会被调用。

2.1.3 流载入器(已废弃)

在Android 4.0之前的版本,数据载入都是在Java层实现的,从4.0开始,Android webkit引入了chromium的部分代码,输入载入走的是C++代码。不过原有的Java代码仍然保留,可以在编译webkit时用USE_CHROME_NETWORK_STACK宏进行切换。

2.2 C层框架

2.2.1 C类与Java类的关系

WebKit类一般被拆成两个,Java类和C++类。比如在Java API部分,有一个WebView类,在C++部分,也有一个WebView类。WebViewCore, WebSettings等等也是同样的。

需要注意的是,JNI是C语言接口,所以Java类并不能直接调用C++代码,需要在C++代码中export出C语言接口。所以代码中使用了一个技巧,在Java类中定义一个int成员变量(实际上是一个指针),指向对应的C++类,如下图所示:

AndroidNativeClass

1.BrowserFrame

与BrowserFrame Java类相对应的C++类为WebFrame(文件名为WebCoreFrameBridge.cpp),该类为Dalvik虚拟机回调BrowserFrame类中定义的本地方法进行了封装。与BrowserFrame中回调函数(Java层)相对应的C层结构定义如下:

struct WebFrame::JavaBrowserFrame
{
    jweak mObj;
    jweak mHistoryList; // WebBackForwardList object
    jmethodID mStartLoadingResource;
    jmethodID mMaybeSavePassword;
    jmethodID mShouldInterceptRequest;
    jmethodID mLoadStarted;
    jmethodID mTransitionToCommitted;
    jmethodID mLoadFinished;
    jmethodID mReportError;
    jmethodID mSetTitle;
    jmethodID mWindowObjectCleared;
    jmethodID mSetProgress;
    jmethodID mDidReceiveIcon;
    jmethodID mDidReceiveTouchIconUrl;
    jmethodID mUpdateVisitedHistory;
    jmethodID mHandleUrl;
    jmethodID mCreateWindow;
    jmethodID mCloseWindow;
    jmethodID mDecidePolicyForFormResubmission;
    jmethodID mRequestFocus;
    jmethodID mGetRawResFilename;
    jmethodID mDensity;
    jmethodID mGetFileSize;
    jmethodID mGetFile;
    jmethodID mDidReceiveAuthenticationChallenge;
    jmethodID mReportSslCertError;
    jmethodID mRequestClientCert;
    jmethodID mDownloadStart;
    jmethodID mDidReceiveData;
    jmethodID mDidFinishLoading;
    jmethodID mSetCertificate;
    jmethodID mShouldSaveFormData;
    jmethodID mSaveFormData;
    jmethodID mAutoLogin;
    AutoJObject frame(JNIEnv* env) {
        return getRealObject(env, mObj);
    }
    AutoJObject history(JNIEnv* env) {
         return getRealObject(env, mHistoryList);
    }
};

该结构作为WebFrame(C层)的一个成员变量(mJavaFrame),在WebFrame构造函数中,用BrowserFrame(Java层)类的回调方法的method ID初始化JavaBrowserFrame结构的各个域。初始后,当WebCore(C层)在剖析网页数据时,有Frame相关的资源改变,比如WEB页面的主题变化,则会通过mJavaFrame结构,调用指定BrowserFrame对象的相应方法,通知Java层处理。

2.JWebCoreJavaBridge

与该对象相对应的C层对象为JavaBridge,JavaBridge对象继承了TimerClient, CookieClient, KeyGenerateorClient, FileSystemClient类,主要负责WebCore中的定时器和Cookie管理。与Java层JWebCoreJavaBridge类中方法method ID相关的是JavaBridege中几个成员变量,在构造JavaBridge对象时,会初始化这些成员变量,之后有Timer或者Cookies事件产生,WebCore会通过这些ID值,回调对应JWebCoreJavaBridge的相应方法。

3.LoadListener

与该对象相关的C层对象为WebCoreResourceLoader,与LoaderListener中回调函数(Java层)相对应的C层结构是struct resourceloader_t,该结构保存了LoadListener对象ID、CancelMethod ID以及DownloadFiledMethod ID等值。当有Cancel或者Download事件产生,WebCore会回调LoadListener类中的CancelMethod或者DownloadFileMethod。

4.WebViewCore

与WebViewCore相关的C类是WebViewCorel,定义了两个数据结构,一个是WebViewCoreFields,对应于Java层WebViewCore对象的成员变量,另一个是WebViewCore::JavaGlue,对应于Java层WebViewCore对象的成员方法。定义如下:

// Field ids for WebViewCore
struct WebViewCoreFields {
    jfieldID m_nativeClass;
    jfieldID m_viewportWidth;
    jfieldID m_viewportHeight;
    jfieldID m_viewportInitialScale;
    jfieldID m_viewportMinimumScale;
    jfieldID m_viewportMaximumScale;
    jfieldID m_viewportUserScalable;
    jfieldID m_viewportDensityDpi;
    jfieldID m_webView;
    jfieldID m_drawIsPaused;
    jfieldID m_lowMemoryUsageMb;
    jfieldID m_highMemoryUsageMb;
    jfieldID m_highUsageDeltaMb;
} gWebViewCoreFields;

// —————————————————————————-

struct WebViewCore::JavaGlue {
    jweak m_obj;
    jmethodID m_scrollTo;
    jmethodID m_contentDraw;
    jmethodID m_layersDraw;
    jmethodID m_requestListBox;
    jmethodID m_openFileChooser;
    jmethodID m_requestSingleListBox;
    jmethodID m_jsAlert;
    jmethodID m_jsConfirm;
    jmethodID m_jsPrompt;
    jmethodID m_jsUnload;
    jmethodID m_jsInterrupt;
    jmethodID m_didFirstLayout;
    jmethodID m_updateViewport;
    jmethodID m_sendNotifyProgressFinished;
    jmethodID m_sendViewInvalidate;
    jmethodID m_updateTextfield;
    jmethodID m_updateTextSelection;
    jmethodID m_clearTextEntry;
    jmethodID m_restoreScale;
    jmethodID m_needTouchEvents;
    jmethodID m_requestKeyboard;
    jmethodID m_requestKeyboardWithSelection;
    jmethodID m_exceededDatabaseQuota;
    jmethodID m_reachedMaxAppCacheSize;
    jmethodID m_populateVisitedLinks;
    jmethodID m_geolocationPermissionsShowPrompt;
    jmethodID m_geolocationPermissionsHidePrompt;
    jmethodID m_getDeviceMotionService;
    jmethodID m_getDeviceOrientationService;
    jmethodID m_addMessageToConsole;
    jmethodID m_formDidBlur;
    jmethodID m_getPluginClass;
    jmethodID m_showFullScreenPlugin;
    jmethodID m_hideFullScreenPlugin;
    jmethodID m_createSurface;
    jmethodID m_addSurface;
    jmethodID m_updateSurface;
    jmethodID m_destroySurface;
    jmethodID m_getContext;
    jmethodID m_keepScreenOn;
    jmethodID m_sendFindAgain;
    jmethodID m_showRect;
    jmethodID m_centerFitRect;
    jmethodID m_setScrollbarModes;
    jmethodID m_setInstallableWebApp;
    jmethodID m_enterFullscreenForVideoLayer;
    jmethodID m_setWebTextViewAutoFillable;
    jmethodID m_selectAt;
    AutoJObject object(JNIEnv* env) {
        // We hold a weak reference to the Java WebViewCore to avoid memeory
        // leaks due to circular references when WebView.destroy() is not
        // called manually. The WebView and hence the WebViewCore could become
        // weakly reachable at any time, after which the GC could null our weak
        // reference, so we have to check the return value of this method at
        // every use. Note that our weak reference will be nulled before the
        // WebViewCore is finalized.
        return getRealObject(env, m_obj);
    }
};

WebViewCore类有个JavaGlue对象作为成员变量,在构建WebViewCore对象时,用WebViewCore(Java层)中的方法ID值初始化该成员变量。并且会将构建的WebViewCore对象指针复制给WebViewCore(Java层)的mNativeClass,这样将WebViewCore(Java层)和WebViewCore(C层)关联起来。

5.WebSettings

与WebSettings相关的C层结构是struct FieldIds(文件名WebSettings.cpp),该结构保存了WebSettings类中定义的属性ID以及方法ID,在构建FieldIds对象时,会设置这些方法和属性的ID值。

6.WebView

与WebView相关的C层类是WebView,该类中的m_javaGlue中保存着WebView(Java层)中定义的属性和方法ID,在WebView(C层)构造方法中初始化,并且将构造的WebView对象(C层)的指针,赋值给WebView类(Java层)的mNativeClass变量,这样WebView(Java层)和WebView对象(C层)建立了关系。

三、基本流程分析

3.1 webkit初始化

Android提供了WebView类,该类提供客户化浏览显示的功能。如果客户需要加入浏览器的支持,可像使用其它视图类一样加入应用程序,显示给用户。当客户代码中第一次生成WebView对象时,会初始化WebKit库(包括Java层和C层两个部分),之后用户可以操作WebView对象完成网络或者本地资源的访问。

WebView对象的生成主要涉及4个类CallbackProxy、WebViewCore、WebViewDatabase以及BrowserFrame。其中CallbackProxy对象为WebKit模块中UI线程和WebKit类库提供交互功能,WebViewCore是WebKit的核心层,负责与C层交互以及WebKit模块C层类库初始化,WebViewDatabase为WebKit模块运行时缓存、cookie等数据存储提供支持,BrowserFrame用于创建WebCore中的Frame,并为Frame提供Java层回调方法。WebKit模块初始化流程如下:

实例化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
3.1.1 JNI native方法注册

在创建WebViewCore时进行,调用System.loadLibrary方法载入webcore相关类库,该过程由Dalvik虚拟机完成,它会从动态链接库目录中寻找libWebCore.so类库,载入到内存中,并且调用WebKit初始化模块的JNI_OnLoad方法(代码见WebCoreJniOnLoad.cpp)。WebKit模块的JNI_OnLoad方法中完成了如下初始化操作:

1. 初始化JavaBridge[registerJavaBridge]

获取JWebCoreJavaBridge类的mNativeBridge成员变量的fieldID,以及注册JWebCoreJavaBridge类中的native方法

2. 初始化JniUtil[registerJniUtil]

注册JniUtil类中的native方法

3. 初始化WebFrame[registerWebFrame]

获取BrowserFrame类的mNativeFrame成员变量的ID,以及注册BrowserFrame类中的native方法

4. 初始化WebCoreResourceLoader[registerResourceLoader]

获取LoadListener类的mNativeLoader成员的ID,以及注册LoadListener类中的native方法

5. 初始化WebViewCore[registerWebViewCore]

获取WebViewCore类的java成员的ID,以及注册WebViewCore类中的native方法

6. 初始化WebHistory[registerWebHistory]

获取WebHistoryItem类的java成员的ID,以及注册WebBackForwardList和WebHistoryItem类中的native方法

7. 初始化WebIconDatabase[registerWebIconDatabase]

注册WebIconDatabase类中的native方法

8. 初始化WebSettings[registerWebSettings]

获取WebSettings类的java成员的ID,以及注册native方法

9. 初始化WebStorage[registerWebStorage]

注册WebStorage类的native方法

10. 初始化WebView[registerWebView]

获取WebView类的mNativeClass成员的ID,以及注册native方法

11. 初始化ViewStateSerializer[registerViewStateSerializer]

注册ViewStateSerializer类的native方法

12. 初始化GeolocationPermissions[registerGeolocationPermissions]

注册GeolocationPermissions类的native方法

13. 初始化MockGeolocation[registerMochGeolocation]

注册MockGeolocation类的native方法

14. 初始化HTML5Audio[registerMediaPlayerAudio]

注册HTML5 Audio类的native方法

15. 初始化HTML5Video[registerMediaPlayerVideo]

注册HTML5VideoViewProxy类的native方法

16. 初始化DeviceMotionAndOrientationManager[registerDeviceMotionAndOrientationManager]

注册DeviceMotionAndOrientationManager类的native方法

17. 初始化CookieManager[registerCookieManager]

注册CookieManager类的native方法

18. 初始化CacheManager[registerCacheManager]

注册CacheManager类的native方法

3.1.2  UI线程和webcore线程

webcore线程在第一次创建WebViewCore对象时创建, 且只创建一次,该线程负责处理WebCore初始化事件。WebViewCore构造函数会被阻塞,直到WebCoreThread初始化完成。在WebViewCore对象构造最后一步,发送INITIALIZE消息给WebCoreThread,执行webcore相关的初始化(WebViewCore::initialize)。在WebViewCore::initialize方法中,会创建BrowserFrame对象,并且向WebView对象发送WEBCORE_INITIALIZED_MSG_ID消息。WebView收到消息后,会执行nativeCreate方法,创建c层的WebView对象。

3.1.3 初始化过程序列图

Initialize Sequence

3.2 loadData

loadData用于加载"data:”形式的url,通过该方法,可以将文件内容读入到字符串,然后通过loadData进行加载,是最简单的一种数据加载方法。比如:

webview.loadData(“<html><body>hello</body></html>”, "text/html”, "utf-8”);

3.2.1 loadData序列图

LoadData Sequence

(未完待续)

chromium build脚本中开始出现android了

2011年10月13日 alex 没有评论

chromium的更新真是够疯狂的,一不小心开发版本就到16了。今天在看chromium源码时,无意中看到有关android的代码了,看来chromium android版本离我们不远了。

首先是build下多了install-build-deps-android.sh脚本,这个脚本是用来安装android SDK r13和android ndk r6b。难道不考虑android3.2以下版本?

build下还有一个all_android.gyp文件,里面只有一个target: android_builder_tests.

build下android文件夹只有一个文件envsetup.sh,在编译android版本前必须执行该脚本设置环境变量。

看了看源码树,只有base和net目录下有少量的android平台代码。

从现存的代码来看,似乎android移植才开了个头,不过最近有媒体爆料说即将发布的android 4.0中即将包含chromium浏览器。莫非chromium也会学习android,只进行有限的开放。

android chromium要来了?

2011年9月16日 alex 5 条评论

之前做android浏览器的时候,还考虑过将chromium移植到android。不过浏览了一下chromium的源码后就放弃了,chromium代码库太庞大了,不是几个人能搞定的。这两天网上满天飞的消息,google开始着手将chrome移植到android上了。不过究其消息来源,并非google官方声明,而是google开发团队的一位大牛Andrei Popescu在webkit的mail list写了下面的话:

We plan to start by setting up a webkit.org build bot that will compile Chromium’s DRT for Android using the Android NDK, SDK and toolchain. We anticipate a reasonably small set of changes to the Chromium port to achieve this. We’re fully committed to maintaining this new flavor of the Chromium port of WebKit and having a build bot up and running as soon as possible will make this an easier task. At the same time, we will be removing the existing incomplete Android port. This includes the Android-specific code in WebCore/platform/android, as well as any code guarded by the PLATFORM(ANDROID) macro.

简单说,就是Google的Android团队要对于Android里的浏览器进行改造,将这个开源的移动浏览器跟另外一个同样开源的桌面浏览器进行整合,移除Android浏览器独有的特殊代码,取而代之Chromium的代码。

以google的实力,决定将chromium移到android上,肯定是办得到的。现在的问题是,会在android的哪个版本中出现?毫无疑问,这个时候决定整合浏览器,一定是冲着android平板而来(对于智能手机,现有的android浏览器可以应付得了),况且移植工作量也不小,所以估计要在android 4.0之后才可以看得到。

我比较关心的是,如果android中有了chromium浏览器,我们做浏览器的是否会失业呢?

分类: Android浏览器 标签: android, chromium

webkit android移植(1):开篇

2011年8月17日 alex 4 条评论

看到这个标题,您可能会觉得奇怪,android浏览器本来就是基于webkit的,而且android还提供了一个非常方便的WebView及相关类。系统的浏览器当然稳定而且强大,但在某些情况下,可能还是不够,比如android浏览器不支持wml,也不支持html5 video。其实webkit本身是支持的,只不过android没有打开编译开关。另外一个原因,就是android中使用的webkit内核比较老,对于html5的支持比较差。因此萌发了自己编译libwebcore,作为独立的应用进行开发。这样做的一个缺点就是和android平台相关了,没有办法做到2.1/2.2/2.3通用。

首先说说开发环境,webkit选择android4.0版本包含的webkit。构建系统选择的是android的源码构建系统。这样选择也是为了选择一个稳定的版本,不在build system上花费太多的时间。开发机环境为ubuntu 11.10,64位系统。

阅读webkit android的代码,我们可以知道,实际上浏览器的代码包括两部分:一部分是java代码,是对webkit的一个封装,提供方便使用的WebView及相关类;一部分是c++代码,也就是webkit代码(包括WebCore和JavaScriptCore,在android 2.2之后,google用V8取代了JavaScriptCore),编译成so,供Java侧代码使用。

分析webkit的代码接口,主要需要做如下工作:

1. WTF库的移植

2. WebCore platform代码的移植

3. WebCore核心代码的移植

4. WebCore Support代码的实现

5. WebKit JNI

6. WebKit Java封装

分类: Android浏览器 标签: android, NDK, webkit

使用内部(com.android.internal)和隐藏(@hide)API[第4部分,定制ADT]

2011年7月6日 alex 6 条评论

本文翻译自http://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-4-customizing-adt/

在前面的文章中,我讲述了如何创建定制的original-android.jar和创建定制的android平台以使用original-android.jar。这仅仅能够使用隐藏API,还为内部API留有一个障碍: ADT。ADT定义了一个规则禁止使用来自com.android.internal的类。

image_thumb30_thumb

有几种方法可以越过该限制规则:

1)完整的ADT源代码可以下载,可以移除或者修改代码,编译然后安装新的定制版本ADT。不好的地方是您必须配置一台64位linux系统,下载源码,编译之。这会花费一些时间,当新版本的ADT出来后,您又需要重新来过。

2)另外一种方式是修改ADT的字节码,只需替换"com/android/inter/**”字符串为其它的字符串,比如"com/android/internax”。

第二种方法可通过脚本自动化完成,也不需要访问源码,在windows下也能工作,这也是我在本文中说明第二种方式的原因。

修改ADT字节码

进入到您的eclipse的plugins文件夹,找出名为com.android.ide.eclipse.adt_*.jar的文件。做一个备份(以防修改错了),另外复制一份改文件到一个单独的"experimental”文件夹,在那里进行字节码修改。

修改*.jar为*.zip,解压文件到一个单独的文件夹,下面就是我所得到的:

image11

现在进入到com/android/ide/eclipse/adt/internal/project子目录,找出AndroidClasspathContainerInitializer.class文件。

image12

该文件包含了字符串"com/android/internal/**”,下一步就是替换该字符串为其它的字符串,如"com/android/internax/**”。改变字符串的长度可能没什么问题,但最好只替换一个字母,保持长度相同。

我是用notepad++进行替换的,因为它支持非打印字符,而且在编辑打印字符时不会修改非打印字符。

image13

修改完后,保存文件,zip压缩文件夹,文件名和原始版本一样。以我的为例:com.android.ide.eclipse.adt_8.0.1.v201012062107-82219.zip,然后重命名为*.jar。

注意:请确保您正确的压缩了文件,可以比较一下修改的zip和原始的zip的内部目录结构。

现在删除eclipse plugins文件夹下的原始ADT*.jar文件,复制经过修改的版本,重启eclipse。

如果没有问题,则会如下图所示:

image14

步骤总结:

  1. 停止eclipse
  2. 从eclipse的plugins文件夹取得adt插件的jar文件。
  3. 重命名.jar为.zip,然后解压到一个单独的目录。
  4. 找到com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.class
  5. 将字符串"com/android/internal/**”替换为"com/android/internax/**”
  6. zip压缩所有文件
  7. 重命名.zip为.jar
  8. 用修改版本替换eclipse plugins文件夹下的原始adt jar文件
  9. 启动eclipse。
相关文章
相关标签/搜索