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注入有几个需要注意的地方:
- 上文提到的多次注入控制,我们使用了mCallProgressCallback变量控制
- 重新加载一个URL之前,需要重置mCallProgressCallback,让重新加载后的页面再次注入js
- 注入的进度阈值可以自由定制,理论上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