近期一直在参照chromium android的ContentShell做移植,当然做浏览器,ContentShell肯定是不够的。有时为了试验chromium android版本的功能,就会在ContentShell上添加一些界面元素。随着功能越来越多,ContentShell上摆放的button也越来越多,界面越来越难看,代码也越来越乱。由此想重新规划一下Browser的UI,但是一来对Android的UI研究不多,二来在UI上花费过多的精力,重心就会有所偏离,毕竟我的研究方向是移植与内核优化。于是就寻求现成的浏览器代码,找了一圈开源浏览器,发现Android自带的浏览器最适合。Android Browser由Google出品,质量自然是没的说,代码结构清晰,功能完备,就是其UI对国人来说不太友好。下面就分析一下将chromium核心挂接到Android Browser上。
一、Android4.0 Browser架构分析
Android 4.0的浏览器引入了很多新特性,如隐私浏览、预加载、离线阅读、google帐号同步等。Android 4.0也是手机和平板系统融合的一个版本,自然的Android 4.0 Browser也提供了两套界面,分别适应手机和平板系统。为此,Android 4.0 Browser采用了视图/控制器分离的架构,可以灵活的支持多套界面,总体的类图如下:
Android Browser采用了经典的MVC设计模式,可以很方便的支持手机和平板两套UI,这也为改造UI提供了便利。Browser的UI分为三个层次,分别是总体UI、Tab和WebView,对应的控制器为UiController、TabController和WebViewController。总体UI对应着主界面,抽象出一个接口UI,派生子类XLargeUi对应于平板的UI,PhoneUi对应于手机的UI。在BrowserActivity类,有一个方法isTablet用于判断平板还是手机。实例化UI子类的代码如下:
boolean xlarge = isTablet(this);
if (xlarge) {
mUi = new XLargeUi(this, mController);
} else {
mUi = new PhoneUi(this, mController);
}
mController.setUi(mUi);
Browser UI的复用技巧
为了达到UI的复用,Browser的界面被划分为多个区域,然后组合起来形成一个完整的用户界面。这是通过两种方式实现的:
1. 使用代码动态加载
比如主界面的xml定义如下:
<merge
xmlns:android="https://schemas.android.com/apk/res/android">
<FrameLayout android:id="@+id/fullscreen_custom_content"
android:visibility="gone"
android:background="@color/black"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<LinearLayout android:orientation="vertical"
android:id="@+id/vertical_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:id="@+id/error_console"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<FrameLayout android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
</merge>
然后在代码中动态添加到界面,这是通过LayoutInflater的inflate方法实现的:
FrameLayout frameLayout = (FrameLayout) mActivity.getWindow()
.getDecorView().findViewById(android.R.id.content);
LayoutInflater.from(mActivity)
.inflate(R.layout.custom_screen, frameLayout);
mContentView = (FrameLayout) frameLayout.findViewById(
R.id.main_content);
Tab内容则是在attachTabToContentView(Tab tab)方法中动态附着到mContentView上的。
2. xml包含
android提供了一个很好的特性,就是xml layout文件可以包含另外一个xml layout文件,这样将共用的一些UI分离出来,放到单独的xml文件,可以最大程度的达到复用的目的。例如title_bar.xml文件就包含了对title_bar_nav的引用:
<RelativeLayout
xmlns:android="https://schemas.android.com/apk/res/android"
android:id="@+id/titlebar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include
layout="@layout/title_bar_nav"
android:id="@+id/taburlbar"
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_height" />
...
</RelativeLayout>
二、Chromium android分析
关于chromium的分析,可以参考<<Chromium源码分析:Content API>>。chromium在三个层次上提供了API,分别是:WebKit API/Content API/Chrome API。我们之前是在Content API层面做浏览器,不过最新的chromium代码在chrome层面上进行了分层,将一些浏览器的UI剥离出来,这样既可以利用chrome的一些实现(比如多Tab、密码保存,扩展),又可以有自己独立的UI。Android版本提供了一个简单的参考实现ChromiumTestShell,非常具有参考价值,类结构图如下:
从类图中可以大致推断出和Android Browser的对应关系:
当然,上图只是一个大致的对应关系,并非严格的一一对应。
三、捏合Android Browser和Chromium TestShell
为了让Chromium的代码挂接到Browser上,设计了一个glue层,就是按照WebView API封装chromium的接口,这样Browser的代码只需稍作修改。设计的类图如下:
其中TabManager的代码合并到TabControl(也可以使用包含或者代理模式),修改TabControl原来的创建/显示/隐藏/关闭Tab的代码,改用TabManager的相应代码。
四、后续
Android Browser的代码使用了很多SDK未公开的API,可以采用一些方法避免这一问题,后面考虑去掉这些私有API调用,也考虑使用Android2.3 SDK,使之支持更多的手机。另外chromium android的移植代码不完善,后续会花主要精力做这一块的完善工作。
补充:
2013-05
工作重点转向在chromium之上提供Android WebView兼容API的工作,请关注github上的项目:https://github.com/mogoweb/chromium_webview
致力于为WebApp/hybrid APP提供强大的引擎。