前几行代码就不说了
//开启js通讯
webview.getSettings().setJavaScriptEnabled(true);
//web调用js函数或代码
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){
webView.loadUrl(js);
}else {
webView.evaluateJavascript("javascript:" + js, value -> {
//something
});
}
1.WebView之not defined
未定义的内容可能是element节点,也可能是函数.不管是哪一种,导致的原因都是
调用时机问题,排查调用时DOM树及js是否已经加载完成.有些element节点是动态增加的,尽管dom都加载完成了也不一定有,哈哈.可以看看我下面eleInsertedCallback 的代码说明
所以我们在onPageFinished()回调中执行十有八九没问题,但它不准,所以还是可能出问题,接下来就说说该怎么办.
2.WebView之onPageFinished()不准
百度搜一下,诚不欺我,可能还不如onProgressChanged() = 100 来的安心!还有一种方法就是
插入js监听dom加载回调
我们可以在webview.onPageStarted()的回调中注入js,
//使用window.onload方式监听dom加载
val domFinishedCallback1 = """window.addEventListener('load', (event) => {
//do something
});"""
//使用DOMContentLoaded方式监听dom加载
val domFinishedCallback2 = """document.addEventListener('DOMContentLoaded', (event) => {
//do something
});"""
//使用DOMNodeInserted方式监听element节点的创建
val eleInsertedCallback = """document.addEventListener('DOMNodeInserted', function(){
var ele = document.getElementsByClassName('xxxClass')[0];
if(ele != undefined){
//do something
}});"""
override fun onPageStarted(val view: WebView, url: String, favicon: Bitmap){
view.evaluateJavascript(
"javascript:" + domFinishedCallback1,
value -> {}
)
}
思路打开了,发挥空间就不止这些了.
当然如果你还想回调到native层,那不妨在native挂载一个对象,然后在这段js语句中调用native挂载对象的方法就实现了无缝衔接!这属于题外话不赘述.
3.WebView之canGoBack()不准
这个问题影响因素有很多,可能是h5的问题也可能是native端的问题.
首先我推荐使用evaluateJavascript(),因为loadUrl()执行js可能会导致这个问题.
如果问题仍然存在,可能的原因如这篇博客所述【BUG笔记】WebView返回时重复加载问题(CanGoBack返回true) - 简书 (jianshu.com)
求人不如求己
按照上文的思路,我们进行一个兜底处理,来一个将计就计,既然canGoBack()判断错误了,自然就走了goBack()的逻辑中了,那我们就改造一下goBack()实现就可以了
@Override
public void goBack() {
//在goBack()中再判断一次是不是真的可以返回,是则走返回逻辑,否则finish当前所在页面
evaluateJavascript(
"javascript:(function() {var state = window.history.state;return state.current != state.back && state.back && state.back != '/';})()",
value -> {
boolean canGoBack = Boolean.parseBoolean(value);
if (canGoBack) {
//可以返回,但有部分机型的super.goBack()就是不生效,为什么,我不知道。只能自己实现goBack()逻辑了
evaluateJavascript(
"javascript:(function() {return window.location.href.replace(window.history.state.current, window.history.state.back);})()",
value1 -> {
String realUrl = "";
//为什么有这个判断,反正js回调的数据就是这样的,只能这样搞了
if (value1.startsWith("\"") && value1.endsWith("\"")) {
realUrl = value1.substring(1, value1.length() - 1);
}
if (!TextUtils.isEmpty(realUrl)) {
loadUrl(realUrl);
} else {
//最后兜个底
super.goBack();
}
});
} else {
Activity activityFromContext = getActivityFromContext(mContext);
if (activityFromContext != null) {
activityFromContext.finish();
}
}
});
}
有朋友肯定会问,既然canGoBack()判断错了,为什么不直接重写canGoBack()呢?
实际上这里是2个问题,一个问题是canGoBack()判断错误;另一个问题是部分机型的goBack()不生效,明明可以返回就是返回没反应
下面是一个错误的处理,谨防上当。调试后你会发现evaluateJavascript是在主线程调用的,而回调也是在主线程,导致await()阻塞主线程的同时,这两处调用也阻塞了,导致jsGoBack成了摆设,尴尬ing...这时肯定相对在子线程调用evaluateJavascript,你会发现需要在相同的线程中调用它才行。所以我没有想到更好的版本处理这个同步!
4.webview输入法焦点遮盖奇葩
大部分情况百度一下都有,仅记录自己遇到的。所有的办法都尝试了,折腾了一个下午终于算是找到问题的原因了。当导航栏被强制禁用时,webview的输入法会遮盖焦点!!!