【版权声明】本文由本人首发于上海竖排网络科技有限公司官方知乎账号,现摘抄至个人掘金账号下,归类学习复习 本文由x-teamer团队成员:清泓 撰写。【持续更新】
【摘要】 Android端Webview是Google官方为移动端提供的展现html 网页的组件。 Webview 是一个基于webkit引擎,可以解析DOM 元素,展示html页面的控件,它和浏览器展示页面的原理是相同的,所以可以把它当做浏览器看待。(chrome浏览器也是基于webkit引擎开发的,Mozilla浏览器是基于Gecko引擎开发的)[1]Android的Webview在低版本和高版本采用了不同的webkit版本内核,4.4后直接使用了Chrome内核。Chromium 渲染引擎实际上是由Browser进程、Render进程和GPU进程组成的。其中,Browser进程负责将网页解析的UI合成 加速展现在页面上,Render 进程 只要负责加载和渲染网页,GPU 进程 负责 Browser 进程 和Render进程发出的GPU消息。我们在创建WebView时候就会创建启动Chromium ,Chromium中有webCore引擎和JsCore引擎。WebCore负责对HTML解析,CSS解析,渲染UI,调试信息等部分。
一. WebView
Android Browser 用来主要用WebView来加载和渲染html网页,来实现网页浏览功能。
主要从网页的 URL 到构建完 DOM 树,接着 从 DOM 树到构建完 WebKit 的绘图上下文,从绘图上下文到生成最终的UI图像。
WebView 拥有load() URL和本地html文件的功能。可以根据不同场景进行使用。
webview加载URL
WebViewClient主要辅助WebView执行处理各种响应请求事件的,比如:
WebViewClient.shouldOverrideUrlLoading();
这个方法的本来是拦截所有WebView的Url跳转的。我们可以通过这个API 构造一个特殊自定义格式的Url跳转,shouldOverrideUrlLoading拦截Url后判断其格式,然后Native 解析到对应的方法和数据再去执行本地方法。
二.JavascriptInterface
开发者都知道安卓API 4.4以前谷歌的webview存在安全漏洞,网站可以通过js注入就可以随便拿到客户端的重要信息,甚至轻而易举的调用本地代码进行流氓行为,谷歌后来发现有此漏洞后,增加了防御措施,如果要是js调用本地代码,开发者必须在代码声明JavascriptInterface。[2]
在4.2之前我们要使得webView加载js只需如下代码:
webView.addJavascriptInterface(new JsBridge(), "JsBridge");
JsBridge()是JavascriptInterface实现类,JsBridge是我们给的别名,也就是Js能拿到java端的桥对象。
4.2之后调用需要在调用方法加入@JavascriptInterface注解,如果代码无此申明,那么也就无法使得js生效,也就是说这样就可以避免恶意网页利用js对android客户端的窃取和攻击。
如下图所示:
这里我们是建立了一个JsInterface的实体接口类,然后写入我们需要的方法,所有方法均加入@JavascriptInterface注解。并实现接口。
在activity中实现其接口方法,就可以正常使用接口了。如下图:
addJavascriptInterface 实际上由 WebViewProvider 来执行实现,WebViewProvider主要负责提供WebView后台程序接口, 每个WebView对象仅绑定到一个实现。[3]
源码:
WebViewProvider提供的这两个抽象方法实际上是WebViewFactoryProvider创建出来的具体WebViewChromiumFactoryProvider实现的。[4]
WebViewChromiumFactoryProvider创建WebViewChromium去实现。
源码:
第四步。 WebViewChromium 实现用创建AwBrowserProcess的来执行BrowserStartupController的执行Js的函数,这个函数是个JNI方法,
使用案例
1.JavascriptInterface 添加必须要准备一个JavascriptInterface的注解方法,其实上面已经说明,我项目中使用的就是这种接口类形式实现的。
2.android页面通过webView. addJavascriptInterface中加入绑定桥。
webView.addJavascriptInterface(new JSInterface().setJsCallback(this), "JSInterface");
以二维码扫码为例子,scanQRCode()是原生的一个扫码接口。window.readData是预留给原生app环境调用的全局环境下的方法,app环境内,建立一个扫码页,实现扫码的功能,并申请到扫码权限,进行扫码,把扫码的结果以参数的形式通过我们刚才创建的方法和一些细节操作把数据传输到H5端,返回到这个readData方法中。然后前端再依据扫码的结果数据进行相关操作。
app 端主要实现步骤:
1.权限申请:
2.原生端页面绘制
3.硬件相关配置细节调整
4.返回数据处理
5.我们刚才实现的扫描接口,现在派上用场了:
在app端这个方法内,接收到H5端调用原生扫描的请求数据之后,这里启动扫描权限申请进程,jsScanMethod是一个string类型的值,通过下图的消息方法中获取到相关扫码信息。
在主页返回码消息方法中,通过evaluateJavascript进行消息处理。
evaluateJavascript是Android4.4以上提供的一个Native与js交互的方法。
android调用javascirpt的方法采用loadurl(javacriptCmd),这种方式的弊端就是没有返回值,得通过js改变iframe.src把结果返回,这样执行效率较低。sdk19以上采用evaluateJavascript方法,在回调方法里又返回值,效率优于前一种。图中为4.4以上的方法:[5]