记一次8.0手机webview onPageFinshed方法不回调的问题

引言

某天,测试突然找到我,说线上医生端打开帖子详情页后,加载的进度圈一直不会消失。我的第一反应是一脸懵逼。what fuck???近两个版本这个页面我压根儿没有做任何改动。难道是用户使用的姿势不对???好吧,吐槽归吐槽,问题总归还是要解决的。

排查问题

  1. 问题复现的操作

    和测试进一步沟通后,发现3.5.0版本从其他原生入口跳入到社区的帖子详情页正常。只有在8.0以上的手机从我们App内公用的H5页面内跳入该页面才会出现该问题,而且一旦出现,再从其他所有的入口跳入也会出现该问题。

  2. 探究问题产生的原因

    借来测试的8.0手机,在自己本地调试后,发现8.0机型 webviewonPageFinished方法竟然没有回调?

    什么情况?立即去google搜索了下,翻了半天也没发现个所以然和解决办法。于是我就开始想,为什么前几个版本没有问题,用户版帖子详情页也没有问题。在最新的版本,公共H5页面到底做了什么?看了下提交记录,发现最新的版本里同事为了解决公共H5页在8.0以上手机首次加载白屏过长的问题,就对8.0以上的手机特意将公共H5页从原先的remote进程放到了主进程中。

    话不多说,赶紧把这行代码注释掉,重新放到remote进程试试。几分钟后,运行查看效果确实解决了。

    不过如果这样改,那岂不是解决一个问题又会产生原先的另外一个问题么?所以,该方案pass掉。

    那还有别的办法么?想了想,如果onPageFinished方法无法回调,是不是说可以将原先这部分的操作转移到WebChromeClient中的onProgressChanged方法中呢。毕竟这个方法本来就比onPageFinished方法要靠谱的多。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
    super.onProgressChanged(view, newProgress);
    if (newProgress > 85){
    //开始注入js文件
    //
    }
    if (newProgress == 100){
    //隐藏进度条
    //原先结束的操作
    }
    }

    运行代码后,发现该方法是会回调的,同时进度条的问题解决了。但是…原先我们在页面加载结束后,会通过JsBridge调用H5的方法,在这里竟然调用后竟然不起作用了。通过调试发现Js文件确实已经注入成功了,但是在调用H5的方法时,在H5的方法池中找不到已注册的方法!

    这下彻底懵逼了,该怎么搞呢?

    到第二天,同事辅助排查的时候,发现在公共H5页面有如下代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     @Override
    public void onResume() {
    if (webView != null) {
    //激活WebView为活跃状态,能正常执行网页的响应
    webView.onResume();
    //激活WebView为活跃状态,能正常执行网页的响应
    webView.resumeTimers();
    }
    }

    @Override
    public void onPause() {
    BridgeWebView webView = this.mHolder.getWebView();
    if (webView != null) {
    //当页面被失去焦点被切换到后台不可见状态,需要执行onPause
    //通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行
    webView.onPause();
    //当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview
    //它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
    webView.pauseTimers();
    }
    }

    这两段代码是之前为了解决页面处于后台视频还会继续播放的问题。看webView.pauseTimers()方法,在页面失去焦点的时候调用它,会将针对全局Webview

    那么问题基本就明了了,从公共H5跳到帖子详情页,触发了该方法,而在医生版的帖子详情页中的onResume方法中并没有调用 webView.resumeTimers(),所以此时webview仍处于pauseTimers设置的后台状态,最终导致onPageFinished方法未回调出现问题。

解决方案

暂时先在医生版帖子详情页中的onResume方法中添加webView.resumeTimers(),后期准备将两端帖子详情页统一放到web模块,公用一个配置。

结论

经过上面的测试分析,发现在8.0以上的手机如果两个内嵌Webview的原生页面,前一个H5页面调用了webViewpauseTimers()方法,而后面的页面没有调用resumeTimers来恢复的话,会导致WebViewClient方法的onPageFinished不会回调。