如何设计一个优雅健壮的Android WebView?(下

nativeReady = false;
var jsoncommand = JSON.stringify(command);
var _temp = prompt(jsoncommand,‘’);
return true;
} else {
return false;
}
}

上面的代码有所删减,若需要执行完整的jsbridge功能,还需要做一些额外的配置。例如告知前端这段js代码已经注入成功的标记。

什么时候注入js合适?

如果做过WebView开发,并且需要和js交互的同学,大部分都会认为js在WebViewClient.onPageFinished()方法中注入最合适,此时dom树已经构建完成,页面已经完全展现出来[1](()[3](()。但如果做过页面加载速度的测试,会发现WebViewClient.onPageFinished()方法通常需要等待很久才会回调(首次加载通常超过3s),这是因为WebView需要加载完一个网页里主文档和所有的资源才会回调这个方法。能不能在WebViewClient.onPageStarted()中注入呢?答案是不确定。经过测试,有些机型可以,有些机型不行。在WebViewClient.onPageStarted()中注入还有一个致命的问题——这个方法可能会回调多次,会造成js代码的多次注入。

另一方面,从7.0开始,WebView加载js方式发生了一些小改变,官方建议把js注入的时机放在页面开始加载之后。援引官方的文档[^4](():

Javascript run before page load

Starting with apps targeting Android 7.0, the Javascript context will be reset when a new page is loaded. Currently, the context is carried over for the first page loaded in a new WebView instance.

Developers looking to inject Javascript into the WebView should execute the script after the page has started to load.

在[这篇文章](()中也提及了js注入的时机可以在多个回调里实现,包括:

  • onLoadResource
  • doUpdateVisitedHistory
  • onPageStarted
  • onPageFinished
  • onReceivedTitle
  • onProgressChanged

尽管文章作者已经做了测试证明以上时机注入是可行的,但他不能完全保证没有问题。事实也是,这些回调里有多个是会回调多次的,不能保证一次注入成功。

WebViewClient.onPageStarted()太早,WebViewClient.onPageFinished()又太迟,究竟有没有比较合适的注入时机呢?试试WebViewClient.onProgressChanged()?这个方法在dom树渲染的过程中会回调多次,每次都会告诉我们当前加载的进度。这不正是告诉我们页面已经开始加载了吗?考拉正是使用了WebViewClient.onProgressChanged()方法来注入js代码。

@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
if (null != mIWebViewClient) {
mIWebViewClient.onProgressChanged(view, newProgress);
}

if (mCallProgressCallback && newProgress >= mProgressFinishThreshold) {
DebugLog.d(“WebView”, "onProgressChanged: " + newProgress);
mCallProgressCallback = false;
// mJsApi不为null且允许注入js的情况下,开始注入js代码。
if (mJsApi != null && WebJsManager.enableJs(view.getUrl())) {
mJsApi.loadLocalJsCode();
}
if (mIWebViewClient != null) {
mIWebViewClient.onPageFinished(view, newProgress);
}
}
}

可以看到,我们使用了mProgressFinishThreshold这个变量控制注入时机,这与前面提及的当progress达到80的时候,加载出来的页面已经基本可用了是相呼应的。

达到80%很容易,达到100%却很难。

正是因为这个原因,页面的进度加载到80%的时候,实际上dom树已经渲染得差不多了,表明WebView已经解析了标签,这时候注入一定是成功的。在WebViewClient.onProgressChanged()实现js注入有几个需要注意的地方:

  1. 上文提到的多次注入控制,我们使用了mCallProgressCallback变量控制
  2. 重新加载一个URL之前,需要重置mCallProgressCallback,让重新加载后的页面再次注入js
  3. 注入的进度阈值可以自由定制,理论上10%-100%都是合理的,我们使用了80%。

H5页面、Weex页面与Native页面交互——KaolaRouter

H5页面、Weex页面与Native页面的交互是通过URL拦截实现的。在WebView中,WebViewClient.shouldOverrideUrlLoading()方法能够获取到当前加载的URL,然后把URL传递给考拉路由框架,便可以判断URL是否能够跳转到其他非H5页面,考拉路由框架在[《考拉Android客户端路由总线设计》](()一文中有详细介绍,但当时未引入Weex页面,关于如何整合三者的通信,后续文章会有详细介绍。

WebViewClient.shouldOverrideUrlLoading()中,根据URL类型做了判断:

public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (StringUtils.isNotBlank(url) && url.equals(“about:blank”)) { //js调用reload刷新页面时候,个别机型跳到空页面问题修复
url = getUrl();
}
url = WebViewUtils.removeBlank(url);
mCallProgressCallback = true;
//允许启动第三方应用客户端
if (WebViewUtils.canHandleUrl(url)) {
boolean handleByCaller = false;
// 如果不是用户触发的操作,就没有必要交给上层处理了,直接走url拦截规则。
if (null != mIWebViewClient && isTouchByUser()) {
// 先交给业务层拦截处理
handleByCaller = mIWebView

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值