記錄一次 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
的實際作用有點疑惑,它註釋中的 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
相關,比如內部跳轉、頁面渲染等。