@rogeryi
2015-05-07T23:50:28.000000Z
字数 4556
阅读 4504
Android
WebView
Resources
作者: 易旭昕 (@roger2yi)
说明: 访问 Cmd Markdown 版本可以获得最佳阅读体验
Chromium Android WebView 研究系列文章
本文主要描述 CAW 目前所使用的资源管理机制。对于 CAW 来说,Android 有自己的一套资源管理机制,而 Chromium 也有自己的资源管理机制,Android 的资源管理机制是通过 Android SDK API 提供的,可以通过 Resource ID 获取对应的 Android SDK 对象,比如 Drawable,Style 等等... 而 Chromium 的资源管理机制的实现是在 Native 端,通过 Resource ID 获取 Chromium 本身的对象,比如 base::StringPiece,gfx::ImageSkia 等。
目前在 CAW 中,这两种机制都同时在用,简单地说,如果 Java 端的代码需要的是 Android SDK 对象,就使用 Android 的资源管理机制,如果 Native 端的代码需要的是 Chromium 本身的对象,使用的就是 Chromium 的资源管理机制,唯一的例外是在 AwResource.java 定义的一些资源,比如 Error Page,No Domain Page,虽然这些资源是 Native 端需要的,但是 CAW 又需要允许 Embedder(WebView)在运行时才进行设置,所以 Native 端是使用 JNI 调用,通过 Java 端来获取对应资源的 Binary Data。
Chromium 资源打包是调用 grit 工具,编译指定的 grd 资源描述脚本来生成对应的 pak 资源包,同时还生成包括相应 Resource ID 的头文件。
<?xml version="1.0" encoding="UTF-8"?>
<grit latest_public_release="0" current_release="1">
<outputs>
<output filename="grit/content_resources.h" type="rc_header">
<emit emit_type='prepend'></emit>
</output>
<output filename="content_resources.pak" type="data_package" />
<output filename="content_resources.rc" type="rc_all" />
</outputs>
<translations />
<release seq="1">
<includes>
<include name="IDR_ACCESSIBILITY_HTML" file="browser/resources/accessibility/accessibility.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
...
</includes>
</release>
</grit>
content_resources.grd,生成 content_resources.pak 和 content_resources.h
需要生成哪些 pak 资源包是在 gyp 编译脚本里面指定的。如果需要了解 pak 的文件格式和如何解包,请参考这篇文章。官方文档 GritUsersGuide 讲解了如何编写 grd 文件,对于文本类型的 grd 文件的编写也可以参考这篇文档 Chromium translations explained: part 1,文本翻译文件的制作可以参考这篇文档 Chromium本地化的方法。
为了保证不同 pak 资源包的 Resource ID 不冲突,Chromiunm 在 src\tools\gritsettings 下有个 resource_ids 文件,该文件定义 Chromium 工程中所有的 grd 文件所生成的资源编号开始区段。
{
"SRCDIR": "../..",
...
"content/content_resources.grd": {
"includes": [25000],
},
...
# Resource ids starting at 31000 are reserved for projects built on Chromium.
}
在目前 CAW 的代码中,资源包的加载过程是这样的:
ResourceBundle 在内核初始化过程中加载了指定的资源包后,其它模块就可以通过调用它来获取资源,资源数据基本上跟指定的 Resource ID 相对应,但是也可以通过其它参数比如 ScaleFactor,ImageRTL 来获取不同的版本,一般来说这些参数都是针对图片资源的,用于获取不同分辨率或者不同文字排列方向的图片。
对于运行在 Browser 进程的模块而言,是可以直接访问 ResourceBundle 的,但是对于运行在 Render 进程的 Blink 而言则不然。Blink 需要通过 Platform 接口实例的方法 loadResource 来获取资源,loadResource 接受的参数是 Resource Name,而 Resource Name 和 Resource ID 的映射位于 Platform 的实现类 BlinkPlatformImpl 中,BlinkPlatformImpl 通过 ContentClient 接口的实例 AwContentClient 间接访问 ResourceBundle 获取资源数据。另外如果在 Blink 里面要获取一张资源图片,直接通过 Image::loadPlatformResource 即可。
PassRefPtr<Image> Image::loadPlatformResource(const char *name)
{
const WebData& resource = Platform::current()->loadResource(name);
if (resource.isEmpty())
return Image::nullImage();
RefPtr<Image> image = BitmapImage::create();
image->setData(resource, true);
return image.release();
}
static Image* platformResource(const char* name)
{
if (!gMediaControlImageMap)
gMediaControlImageMap = new MediaControlImageMap();
if (Image* image = gMediaControlImageMap->get(name))
return image;
if (Image* image = Image::loadPlatformResource(name).leakRef()) {
gMediaControlImageMap->set(name, image);
return image;
}
ASSERT_NOT_REACHED();
return 0;
}
static Image* getMediaSliderThumb()
{
static Image* mediaSliderThumb = platformResource("mediaplayerSliderThumb");
return mediaSliderThumb;
}
RenderMediaControls::getMediaSliderThumb 获取视频播放器的进度条指示器图片
在 CAW 里面,无论是 AOSP 中的代码,还是 Chromium 为 Android WebView 适配的平台相关代码,还是 Chromium 的公共代码,这三者都有使用到 Android 资源,不过它们使用的方式有些不一样。
AOSP 中的代码,一般是直接使用 SDK 里面的资源,包括公开和非公开的,比如 JsDialogHelper.java 中使用 “com.android.internal.R.string.js_dialog_before_unload_positive_button”;
Chromium 为 Android WebView 适配的平台相关代码(包 org.chromium.android_webview),使用到的资源需要由 Embedder,也就是位于 AOSP 中的 WebView/WebViewChromium 来提供,AwResource.java 提供了相应的接口,比如 AwResource.setConfigKeySystemUuidMapping 接收一个外部传入的 Resource ID,而这个 ID 指向 APK 资源包中的一个字串列表资源;
Chromium 公共代码里面,目前是 content 和 ui 模块需要使用资源,它们使用的方式有些复杂...
总的来说,我们在打包最终的 APK 时,需要把 content 和 ui 模块目录下的资源拷贝到最终的资源目录,调用 aapt 工具生成最终的资源包和 R.java,然后再把这个 R.java 为 content 和 ui 模块各复制一份,修改包名为 org.chromium.content 和 org.chromium.ui 后再拷贝回对应包名的目录。
通过这种方式,Chromium 即保证了 Resource ID 不会在不同的 R.java 文件中出现冲突,又可以在不知道最终 R.java 所在包名字的情况下(这个包名是打包 APK 时由 AndroidManifest.xml 指定的应用包名决定的,Chromium 本身的代码不能对此进行预设),预先使用位于模块包目录下的 R.java 文件解决 Resource ID 的引用问题。
不过对于 Android WebView 来说,Chromium 公共代码里面使用到资源的那部分代码实际上是不需要的,运行时应该也不会运行到这些代码,理论上我们注释掉相应的代码就可以了。