记录一次 WebView.pauseTimers 引发的问题及该方法的真实含义
问题背景:
在某个 H5 页面可能会有视频信息,为了解决页面退出后视频继续播放的问题,在页面销毁时会对 WebView 进行一些回收销毁操作,其中包括 pauseTimers
操作。
问题描述:
同时打开两个 WebView 页面,关闭第二个页面,第一个页面中的部分操作不再响应。
首先,这个问题是由于 pauseTimers
导致的,因为 pauseTimers
会暂停所有 WebView 的 layout
、parsing
和 JavaScript timers
,这是一个全局生效的方法,所以导致第一个页面中的 js 方法也被暂停了(这个说法是错误的,下文中会纠正)。但是程序有在页面 onResume
时调用 resumeTimers
恢复 js 方法的调用,那么为何没有生效呢?
进一步分析后发现,导致这个问题的原因还包括页面切换时生命周期的执行顺序。假设打开 A 页面后再打开 B 页面,此时关闭 B 页面,两个页面的生命周期执行顺序为:B:onPause -> A:onRestart -> A:onStart -> A:onResume -> B:onStop -> B:onDestroy
。
从这里可以看出,关闭 B 页面后,会先进入 A 页面的 onResume
并执行 resumeTimers
,然后再进入 B 页面的 onDestroy
并执行 pauseTimers
。所以最终执行的是 pauseTimers
,导致的结果是全局的 WebView 都被停止了 js 的调用。
解决方案:
将 pauseTimers
方法改为在页面 onPause
时调用。
拓展思考:
在研究 pauseTimers
的过程中发现,很多人对其的理解是暂停全局所有的 js 方法,网上也普遍是这个说法。但在测试过程中发现并不是这样的,比如我在测试时写了个 html 页面,其中有两个按钮对应调用两段 js 方法,一段是调用 H5 中的 alert 弹窗,一段是调用 Android 中的方法弹出 toast,发现在调用 pauseTimers
之后这两个 js 方法都还可以继续响应执行。html 代码如下:
<html>
<body>
<button style="width:100px;height:50px;" onclick="jsAlert()">alert</button>
<button style="width:100px;height:50px;" onclick="androidToast()">toast</button>
</body>
</html>
<script>
function jsAlert() {
alert("Can Alert!");
}
function androidToast() {
window.market.showToast("Can Toast!");
}
</script>
所以我对于 pauseTimers
的实际作用有点疑惑,它的注释中提到 Pauses all layout, parsing, and JavaScript timers for all WebViews.
,其中的 JavaScript timers
从字面意思来看也不是指 js 方法,而像是指定时器之类的。所以这里进一步测试,在 html 中实现一个计时器:
<html>
<body>
<button style="width:100px;height:50px;" onclick="startTimer()">start</button>
<button style="width:100px;height:50px;" onclick="resetTimer()">reset</button>
<button style="width:100px;height:50px;" onclick="jsAlert()">alert</button><br><br>
<input style="width:100px;height:50px;" value="0" id="show_num">
</body>
</html>
<script>
var timer = null;
var num = 0;
function startTimer() {
timer = setInterval(function() {
document.getElementById("show_num").value = ++num;
}, 1000);
}
function resetTimer() {
clearInterval(timer);
document.getElementById("show_num").value = 0;
countdown = null;
num = 0;
}
function jsAlert() {
alert("Can Alert!");
}
</script>
在 Android 中实现两个按钮,分别去调用 WebView 的 pauseTimers
和 resumeTimers
方法:
findViewById(R.id.pause).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebView.pauseTimers();
}
});
findViewById(R.id.resume).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebView.resumeTimers();
}
});
页面如下:
开始测试,首先点击 H5 页面中 start 按钮,H5 页面上的计时器开始计数,此时点击 alert 可以弹出弹窗。然后点击 Native 界面中的 pause 按钮,执行 pauseTimers
操作,发现 H5 页面上的计时器暂停了,此时 alert 弹窗仍可弹出。再点击 Native 界面中的 resume 按钮,执行 resumeTimers
操作,H5 页面上的计时器继续开始计数。
通过上述研究过程可以发现,pauseTimers
仅暂停了 js 中的计时器,并不会影响 js 方法的调用和响应,所以 “pauseTimers
能停止全局 js” 这个说法是不正确的。而之前提到的页面中部分操作不响应的问题,可能是和 WebView 中 layout
或 parsing
相关,比如内部跳转、页面渲染等。