由于工作的原因玻孟,需要在Android WebView上加載游戲展示笆载,在這期間遇見了很多相關(guān)的坑扑馁,在此集中下。本文長(zhǎng)期更新凉驻,歡迎批評(píng)指正腻要。
Android Webview 進(jìn)程內(nèi)共享
在不考慮多進(jìn)程的情況下,一個(gè)App打開后使用的Webview使用的是同一個(gè)線程(網(wǎng)上名為WebViewCoreThread)涝登。當(dāng)創(chuàng)建webview實(shí)例的時(shí)候雄家,會(huì)嘗試去啟動(dòng)Android 瀏覽器內(nèi)核,我們的前端代碼是借助他完成渲染的胀滚,同時(shí)你會(huì)發(fā)現(xiàn)在開發(fā)者選項(xiàng)中你能看見本機(jī)的webview版本甚至更換他趟济。
所以這會(huì)帶來(lái)什么潛在問題呢?如果你的應(yīng)用內(nèi)存在多個(gè)webview咽笼,他們大概率會(huì)互相影響顷编,比如resumeTimer和pauseTimer函數(shù)是對(duì)全局webview生效的。
Laya引擎游戲?qū)ebView寬高敏感
經(jīng)測(cè)試發(fā)現(xiàn)剑刑,laya引擎游戲在小米機(jī)型上高發(fā)白屏媳纬。
原因是在開始加載時(shí)將WebView設(shè)置為GONE,導(dǎo)致內(nèi)部獲取寬高的時(shí)候?yàn)?施掏,引擎在繪制的時(shí)候出現(xiàn)錯(cuò)誤钮惠;
解決方法是將GONE改為INVISIABLE,雖然不可見但是寬高measure會(huì)進(jìn)行七芭。
實(shí)測(cè)同樣的設(shè)置在cocos游戲上沒有問題
WebView低版本帶來(lái)的各種奇葩問題
在Android 4.4之前渲染內(nèi)核是WebKit素挽,4.4之后才換成chrome,符合谷歌版本帝的形象抖苦,這也造成了在一些低端機(jī)和低端版本上webview的表現(xiàn)有差異毁菱。在此列舉幾個(gè):
- 頁(yè)面關(guān)閉后還有聲音:機(jī)型oppo x9s 版本米死,兩種方法解決,一是調(diào)用reload方法重新初始化數(shù)據(jù)贮庞,二是調(diào)用webview.destroy方法峦筒。更推薦后者,谷歌在官方文檔中建議在webview從View樹移除后調(diào)用窗慎,會(huì)清除webview的網(wǎng)絡(luò)狀態(tài)物喷。
Destroys the internal state of this WebView. This method should be called after this WebView has been removed from the view system. No other methods may be called on this WebView after destroy.
- 短暫出現(xiàn)游戲聲音:這個(gè)更偏向于手機(jī)系統(tǒng)問題,motox2 機(jī)型上滅屏再亮屏(未解鎖)會(huì)短暫出現(xiàn)游戲聲音遮斥。這是因?yàn)榱疗習(xí)r就會(huì)啟動(dòng)onResume方法峦失,再執(zhí)行一次onPause方法,應(yīng)該是對(duì)鎖屏進(jìn)行了假處理
WebView js線程卡死導(dǎo)致所有webview白屏
我們知道webview提供了js橋使得js代碼能調(diào)用Android代碼术吗,調(diào)用的線程是在webview自己的js線程尉辑,如果你嘗試在@JavascriptInterface方法中執(zhí)行耗時(shí)中斷操作,很可能將此線程搞崩较屿,造成應(yīng)用內(nèi)所有webview白屏隧魄,所以請(qǐng)避免在方法中使用耗時(shí)操作。
WebView內(nèi)存泄露
此問題也長(zhǎng)期排WebView問題的前列隘蝎,根本原因同第一點(diǎn)WebView進(jìn)程內(nèi)共享购啄,一旦你開啟了一個(gè)WebView,那就別想跑了嘱么,不管是webview.destory或者是變量置空都無(wú)效狮含。
綜合各家觀點(diǎn),有以下幾個(gè)點(diǎn):
- 使用new WebView來(lái)創(chuàng)建而不是xml曼振,這里context建議使用application几迄,使用activity
- 銷毀時(shí)主動(dòng)getParent.removeView然后調(diào)用destory
- 反射大法,這里不是很推薦冰评,因?yàn)椴荒艽_保api以及變量不修改
在一些大廠的WebView策略相關(guān)文章中乓旗,WebView的銷毀被忽略了,是因?yàn)殇N毀這東西得不償失集索,主要做好當(dāng)前頁(yè)面的資源釋放即可嘱丢,否則WebView的重新啟動(dòng)將十分耗時(shí)敲才。
JS方法setTimeOut失效
在游戲引擎(多數(shù)前端頁(yè)面也是)中,setTimeOut被大量使用校坑,他的效果是在單線程js中延時(shí)執(zhí)行結(jié)果穷遂,但是在合作項(xiàng)目中發(fā)現(xiàn)一個(gè)很嚴(yán)重的問題函匕,這個(gè)方法可能沒有回調(diào)?
最終原因追蹤到了timer蚪黑,猜測(cè)合作方在頁(yè)面結(jié)束時(shí)使用了pauseTimers盅惜,這個(gè)方法是面對(duì)全局webview的中剩,所以另外的webview沒有執(zhí)行resumeTimers方法重啟js計(jì)時(shí)器的話,setTimeOut將失效抒寂。最終結(jié)果將是白屏结啼,pageFinish函數(shù)沒有被調(diào)用,造成整個(gè)頁(yè)面的異常屈芜。
在參考文章一中指出:"還有一種情況是在不使用pauseTimers的情況下從Activity返回上一個(gè)包含WebView的Activity時(shí)setTimeOut是不執(zhí)行的"這種情況我自己模擬不出來(lái)郊愧,不知道是否是理解問題。
WebView緩存大坑
這個(gè)地方詳細(xì)起來(lái)說(shuō)完全可以起一系列文章井佑,具體可以參考文章二和三属铁,這里主要講下自己親身經(jīng)歷過的。
Android中關(guān)于瀏覽器緩存設(shè)置的主要是
getSettings().setCacheMode()躬翁,默認(rèn)是WebSettings.LOAD_LOAD_DEFAULT
這個(gè)的意思是完全按照服務(wù)端要求來(lái)焦蘑,不加設(shè)置
在某些情況下,我們希望能強(qiáng)制刷新盒发,所以可以考慮使用另外一種設(shè)置:WebSettings.LOAD_NO_CACHE
這樣設(shè)置以后會(huì)在資源請(qǐng)求頭里加上cache-control=no-cache參數(shù)例嘱,瀏覽器中
這樣一方面是對(duì)服務(wù)端而言會(huì)盡可能的請(qǐng)求所有資源,避免一些緩存不可訪問的問題迹辐;再者他類似于瀏覽器的刷新蝶防,可以對(duì)本地瀏覽器進(jìn)行刷新。
綜上明吩,在一下情況下可以考慮no-cache:
- 對(duì)資源高度敏感的情況
- 瀏覽器偶先加載無(wú)反應(yīng)的情況(臨時(shí)解決方案)
WebView調(diào)試大法
很多時(shí)候我們的設(shè)置不能完全避免一些稀奇古怪的問題间学,這個(gè)時(shí)候就需要開啟調(diào)試了。
為了允許調(diào)試印荔,我們需要對(duì)webview進(jìn)行設(shè)置:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mWebView.setWebContentsDebuggingEnabled(true);
}
注意低葫,這個(gè)方法一旦開啟,應(yīng)用內(nèi)的WebView就都開啟了
然后用usb連接測(cè)試機(jī)仍律,進(jìn)入調(diào)試模式嘿悬,在Chrome中輸入
chrome://inspect
然后選擇自己的機(jī)型,點(diǎn)擊具體的webview頁(yè)面就能進(jìn)入熟悉的前端調(diào)試頁(yè)面啦
這個(gè)過程中我的魅族16p會(huì)顯示404水泉,換一臺(tái)機(jī)子就行了 善涨,不要迷戀于一只機(jī)子。
還有一個(gè)比較好用的調(diào)試方法草则,就是代理映射钢拧,可以通過charles(or 其他)進(jìn)行映射,因?yàn)楹芏鄷r(shí)候js源碼是被壓縮過的很難看炕横,這時(shí)我們可以通過映射原始版本來(lái)進(jìn)行問題排查源内,方便很多。