安卓系统源码编译系列(六)——单独编译内置浏览器WebView教程
本文主要对从安卓系统源码中抽取出WebView相关源码进行单独编译的流程进行说明。
编译流程说明
由于WebView包含两个部分,一部分是上层的Java代码,包括若干Java类,用于对外提供接口;另一部分是下层的C++代码,包括两个so库(libwebcore.so和libchromium_net.so),用于网页的解析和渲染。两个部分之间通过JNI进行交互。
因此,编译WebView也需要分成两部分,一部分是编译Java代码,另一部分是将C++代码编译成so库。另外,由于WebView的Java代码中会使用到很多系统的隐藏API,所以我们还需要编译安卓系统,并从中获取几个jar包。
编译Java代码
首先,我们需要下载并编译任意一个版本的安卓系统源码,具体步骤可以参见《安卓系统源码下载及编译教程》。
完成编译后,我们可以使用Eclipse(不能使用Intellij IDEA,因为之后添加library的时候无法设置为system library)新建一个Android工程,在src目录下创建一个 android.webkit2 的包。然后将源码目录下的 frameworks/base/core/java/android/webkit 目录下的所有文件拷贝到新创建的包中。
由于其中有一个类是编译之后生成的,所以我们还需要从编译完的源码目录 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/webkit 中将EventLogTags.java类也拷贝到新建的包中。
接下来,我们需要在工程中加入包含系统隐藏API的jar包,将以下三个jar包重命名(名称随意)后拷贝到工程的libs目录下:
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar
out/target/common/obj/JAVA_LIBRARIES/bouncycastle_intermediates/classes.jar
在Eclipse中打开工程的Properties->Java Build Path->Libraries->Add Library->User Library,点击下一步,选择User Libraries...,选择New,随便输入一个名字,将System library选上(重要),选择OK。选中刚刚新建的User Library,选择Add JARs,在工程目录中选择新加入的三个jar包,选择OK。
添加完Library之后,切换到Order and Export选项卡,将刚刚新建的User library调整到最前面,确保隐藏API不会被系统API覆盖(由于包名相同),选择OK。另外,还要把Android Private Libraries库前的勾取消掉,否则之后运行会报错。
最后,由于我们的java文件都放到了android.webkit2包下,避免与系统的包重名,我们需要将所有java文件中的android.webkit都替换成android.webkit2。使用菜单中的Search命令批量替换即可。完成替换后,刷新一下工程,会发现所有的错误都没了。(有可能还会提示minSdkVersion版本太低,直接按要求修改AndroidManifest.xml文件即可)
编译so库
此时我们的Eclipse工程已经可以编译运行了,但是启动的时候会闪退,因为我们还没有加入so库。下面我们就来编译so库。再回到虚拟机中,进入到安卓源码的external/chromium目录下,在终端执行以下命令:
$ sudo sed -i "s#android/webkit#android/webkit2#g" `grep android/webkit -rl *`
可以将源码中的android/webkit都替换成android/webkit2,确保与我们的java代码包名相同。
再打开external/chromium目录下的Android.mk文件,将其中的libchromium_net都替换成libchromium_net2,同时增加一行LOCAL_MODULE_TAGS := optional,修改部分如下:
LOCAL_MODULE := libchromium_net2
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_TAGS := optional
INTERMEDIATES := $(call local-intermediates-dir)
再回到安卓源码根目录,运行以下命令:
$ source build/envsetup.sh
$ mmm external/chromium
编译成功后可以在 out/target/product/generic/obj/lib 目录下找到libchromium_net2.so文件。
接下来进入到安卓源码的external/webkit/Source/WebKit/android目录下,在终端执行以下命令:
$ sudo sed -i "s#android/webkit#android/webkit2#g" `grep android/webkit -rl *`
同样可以将源码中的android/webkit都替换成android/webkit2,确保与我们的java代码包名相同。
再打开external/webkit目录下的Android.mk文件,将其中的libwebcore都替换成libwebcore2(三个地方),并且增加一行LOCAL_MODULE_TAGS := optional,另外还要将LOCAL_SHARED_LIBRARIES里面的libchromium_net改成libchromium_net2。修改部分如下:
# Define our module and find the intermediates directory
LOCAL_MODULE := libwebcore2
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE_TAGS := optional
base_intermediates := $(call local-intermediates-dir)
...
LOCAL_SHARED_LIBRARIES := \
libEGL \
libGLESv2 \
libandroid \
libandroidfw \
libandroid_runtime \
libchromium_net2 \
libcrypto \
...
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := libwebcore2
LOCAL_MODULE_TAGS := optional
LOCAL_LDLIBS := $(WEBKIT_LDLIBS)
LOCAL_SHARED_LIBRARIES := $(WEBKIT_SHARED_LIBRARIES)
LOCAL_STATIC_LIBRARIES := libwebcore2 $(WEBKIT_STATIC_LIBRARIES)
LOCAL_LDFLAGS := -fvisibility=hidden
同样再回到安卓源码根目录,运行以下命令:
$ source build/envsetup.sh
$ mmm external/webkit
编译成功后可以在 out/target/product/generic/obj/lib 目录下找到libwebcore2.so文件。
完成编译并运行测试代码
下面我们将以上编译生成的两个so文件(libchromium_net2.so和libwebcore2.so)放到我们工程的 libs/armeabi 目录下。再修改工程android.webkit2包下的JniUtil.java和WebViewCore.java文件,将其中的
static {
System.loadLibrary("webcore");
System.loadLibrary("chromium_net");
}
改为(注意,顺序也颠倒了)
static {
System.loadLibrary("chromium_net2");
System.loadLibrary("webcore2");
}
到此为止,与WebView相关的操作都完成了,我们可以开始加入测试代码。在测试的Activity.java的onCreate方法中加入:
WebView webView = (WebView)findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl("http://www.baidu.com");
对应的xml layout中加入:
<android.webkit2.WebView
android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
最后在AndroidManifest.xml中加入:
<uses-permission android:name="android.permission.INTERNET"/>
现在我们就可以在模拟器上运行工程了,运行效果如下:

注意:
- 如果出现黑屏,可以尝试将最后加入的INTERNET权限去掉后重试。
- 只能在编译时对应版本的模拟器上运行,如果提示函数找不到,可以切换相应模拟器的版本后重试。
真机运行
在模拟器上运行成功之后可以在真机上运行,效果如下:

但是一旦用手指滑动WebView,应用就会闪退,原因是缺少一些资源,下面我们来将它们加上:
首先我们需要将安卓源码目录下的 frameworks/base/core/res/res/values/styles.xml 中的
<style name="ZoomControls">
<item name="android:gravity">bottom</item>
<item name="android:paddingLeft">15dip</item>
<item name="android:paddingRight">15dip</item>
</style>
粘贴到我们工程目录下的 res/values/styles.xml 中。
然后再将 frameworks/base/core/res/res/layout/zoom_magnify.xml 文件复制到我们工程目录下的 res/layout 中。
接着将 frameworks/base/core/res/res/drawable/btn_zoom_page.xml 文件复制到我们工程目录下的 res/drawable 中。
最后将 frameworks/base/core/res/res 下的 drawable-ldpi 、 drawable-mdpi 、 drawable-hdpi 、 drawable-xhdpi 目录下的 btn_zoom_page_normal.png 和 btn_zoom_page_press.png 文件复制到我们工程目录 res 下的相应文件夹中。
再次运行,即可任意滑动WebView了。
参考资料
《 Android 4.1 - 将系统浏览器编译成独立应用 》
如果大家觉得对自己有帮助的话,还希望能帮顶一下,谢谢:)
安卓系统源码编译系列(六)——单独编译内置浏览器WebView教程的更多相关文章
- GGTalk——C#开源即时通讯系统源码介绍系列(一)
坦白讲,我们公司其实没啥技术实力,之所以还能不断接到各种项目,全凭我们老板神通广大!要知道他每次的饭局上可都是些什么人物! 但是项目接下一大把,就凭咱哥儿几个的水平,想要独立自主.保质保量保期地一个个 ...
- Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析
Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析 前言 上一篇文章Spring Ioc源码分析系列--Ioc源码入口分析已经介绍到Ioc容器 ...
- Alamofire源码解读系列(六)之Task代理(TaskDelegate)
本篇介绍Task代理(TaskDelegate.swift) 前言 我相信可能有80%的同学使用AFNetworking或者Alamofire处理网络事件,并且这两个框架都提供了丰富的功能,我也相信很 ...
- spark 源码分析之十二 -- Spark内置RPC机制剖析之八Spark RPC总结
在spark 源码分析之五 -- Spark内置RPC机制剖析之一创建NettyRpcEnv中,剖析了NettyRpcEnv的创建过程. Dispatcher.NettyStreamManager.T ...
- Spring源码由浅入深系列六 CreateBean过程
- Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager)
Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager) 本篇主要讲解iOS开发中的网络监控 前言 在开发中,有时候我们需要获取这些信息: 手机是否联网 ...
- Alamofire源码解读系列(八)之安全策略(ServerTrustPolicy)
本篇主要讲解Alamofire中安全验证代码 前言 作为开发人员,理解HTTPS的原理和应用算是一项基本技能.HTTPS目前来说是非常安全的,但仍然有大量的公司还在使用HTTP.其实HTTPS也并不是 ...
- Alamofire源码解读系列(十二)之请求(Request)
本篇是Alamofire中的请求抽象层的讲解 前言 在Alamofire中,围绕着Request,设计了很多额外的特性,这也恰恰表明,Request是所有请求的基础部分和发起点.这无疑给我们一个Req ...
- Alamofire源码解读系列(九)之响应封装(Response)
本篇主要带来Alamofire中Response的解读 前言 在每篇文章的前言部分,我都会把我认为的本篇最重要的内容提前讲一下.我更想同大家分享这些顶级框架在设计和编码层次究竟有哪些过人的地方?当然, ...
随机推荐
- 自定义表单验证$setValidaity
- [Usaco2008 Nov]mixup2 混乱的奶牛 简单状压DP
1231: [Usaco2008 Nov]mixup2 混乱的奶牛 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 685 Solved: 383[S ...
- onmeasure
UNSPECIFIE : 0 [0x0],未加规定的,表示没有给子view添加任何规定. EXACTLY : 1073741824 [0x40000000],精确的,表示父view为子view确定精确 ...
- title与h1的区别
title与h1的联系: 从网站角度看,title更重于网站信息.title可以直接告诉搜索引擎和用户这个网站是关于什么主题和内容的. 从文章角度看,h1则是用于概括文章主题.用户进入内容页,想看到的 ...
- 二叉树的遍历(递归,迭代,Morris遍历)
二叉树的遍历: 先序,中序,后序: 二叉树的遍历有三种常见的方法, 最简单的实现就是递归调用, 另外就是飞递归的迭代调用, 最后还有O(1)空间的morris遍历: 二叉树的结构定义: struct ...
- 如何自定义kindeditor编辑器的工具栏items即去除不必要的工具栏或者保留部分工具栏
kindeditor编辑器的工具栏主要是指编辑器输入框上方的那些可以操作的菜单,默认情况下编辑器是给予了所有的工具栏.针对不同的用户,不同的项目,不同的环境,可能就需要保留部分工具栏.那么我们应该如何 ...
- 突然想起android与mfc差异
两者都可以算作是客户端程序,都是做上位机用的.而且都是被动执行. 相同点: 1.MFC中,它是由 project的名字 里面的某个成员函数来初始化,窗体,以及窗体里面的变量. 后面都是监听消息循环.数 ...
- 深入了解Ant构建工具 命令
深入了解Ant构建工具 标签: ant工具任务jarjavaclass 2010-05-29 21:16 1346人阅读 评论(2) 收藏 举报 版权声明:本文为博主原创文章,未经博主允许不得转载. ...
- 解决vs2013使用Git推送到远程仓库报错的问题
在上一篇<让PowerShell使用Git>中可以让PowerShell运行Git命令,那么就开始使用. 1.从远程仓库克隆项目 GitHub和Git.oschina都是不错的免费托管网站 ...
- Pjax.js防刷新技术
自我感觉良好,所以拿出现在自己用的 Pjax.js 分享给大家 当然 这个版本是 经过本人修改后的版本,跟其它 拿过来就用的 不一样 而且区别还不小 大多的 Pjax 都是 跟后台无关的,而这个版本是 ...