由于工作的原因,需要在Android WebView上加载游戏展示,在这期间遇见了很多相关的坑,在此集中下。本文长期更新,欢迎批评指正。
Android Webview 进程内共享
在不考虑多进程的情况下,一个App打开后使用的Webview使用的是同一个线程(网上名为WebViewCoreThread)。当创建webview实例的时候,会尝试去启动Android 浏览器内核,我们的前端代码是借助他完成渲染的,同时你会发现在开发者选项中你能看见本机的webview版本甚至更换他。
所以这会带来什么潜在问题呢?如果你的应用内存在多个webview,他们大概率会互相影响,比如resumeTimer和pauseTimer函数是对全局webview生效的。
Laya引擎游戏对WebView宽高敏感
经测试发现,laya引擎游戏在小米机型上高发白屏。
原因是在开始加载时将WebView设置为GONE,导致内部获取宽高的时候为0,引擎在绘制的时候出现错误;
解决方法是将GONE改为INVISIABLE,虽然不可见但是宽高measure会进行。
实测同样的设置在cocos游戏上没有问题
WebView低版本带来的各种奇葩问题
在Android 4.4之前渲染内核是WebKit,4.4之后才换成chrome,符合谷歌版本帝的形象,这也造成了在一些低端机和低端版本上webview的表现有差异。在此列举几个:
- 页面关闭后还有声音:机型oppo x9s 版本,两种方法解决,一是调用reload方法重新初始化数据,二是调用webview.destroy方法。更推荐后者,谷歌在官方文档中建议在webview从View树移除后调用,会清除webview的网络状态。
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.
- 短暂出现游戏声音:这个更偏向于手机系统问题,motox2 机型上灭屏再亮屏(未解锁)会短暂出现游戏声音。这是因为亮屏时就会启动onResume方法,再执行一次onPause方法,应该是对锁屏进行了假处理
WebView js线程卡死导致所有webview白屏
我们知道webview提供了js桥使得js代码能调用Android代码,调用的线程是在webview自己的js线程,如果你尝试在@JavascriptInterface方法中执行耗时中断操作,很可能将此线程搞崩,造成应用内所有webview白屏,所以请避免在方法中使用耗时操作。
WebView内存泄露
此问题也长期排WebView问题的前列,根本原因同第一点WebView进程内共享,一旦你开启了一个WebView,那就别想跑了,不管是webview.destory或者是变量置空都无效。
综合各家观点,有以下几个点:
- 使用new WebView来创建而不是xml,这里context建议使用application,使用activity
- 销毁时主动getParent.removeView然后调用destory
- 反射大法,这里不是很推荐,因为不能确保api以及变量不修改
在一些大厂的WebView策略相关文章中,WebView的销毁被忽略了,是因为销毁这东西得不偿失,主要做好当前页面的资源释放即可,否则WebView的重新启动将十分耗时。
JS方法setTimeOut失效
在游戏引擎(多数前端页面也是)中,setTimeOut被大量使用,他的效果是在单线程js中延时执行结果,但是在合作项目中发现一个很严重的问题,这个方法可能没有回调?
最终原因追踪到了timer,猜测合作方在页面结束时使用了pauseTimers,这个方法是面对全局webview的,所以另外的webview没有执行resumeTimers方法重启js计时器的话,setTimeOut将失效。最终结果将是白屏,pageFinish函数没有被调用,造成整个页面的异常。
在参考文章一中指出:"还有一种情况是在不使用pauseTimers的情况下从Activity返回上一个包含WebView的Activity时setTimeOut是不执行的"这种情况我自己模拟不出来,不知道是否是理解问题。
WebView缓存大坑
这个地方详细起来说完全可以起一系列文章,具体可以参考文章二和三,这里主要讲下自己亲身经历过的。
Android中关于浏览器缓存设置的主要是
getSettings().setCacheMode(),默认是WebSettings.LOAD_LOAD_DEFAULT
这个的意思是完全按照服务端要求来,不加设置
在某些情况下,我们希望能强制刷新,所以可以考虑使用另外一种设置:WebSettings.LOAD_NO_CACHE
这样设置以后会在资源请求头里加上cache-control=no-cache参数,浏览器中
这样一方面是对服务端而言会尽可能的请求所有资源,避免一些缓存不可访问的问题;再者他类似于浏览器的刷新,可以对本地浏览器进行刷新。
综上,在一下情况下可以考虑no-cache:
- 对资源高度敏感的情况
- 浏览器偶先加载无反应的情况(临时解决方案)
WebView调试大法
很多时候我们的设置不能完全避免一些稀奇古怪的问题,这个时候就需要开启调试了。
为了允许调试,我们需要对webview进行设置:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mWebView.setWebContentsDebuggingEnabled(true);
}
注意,这个方法一旦开启,应用内的WebView就都开启了
然后用usb连接测试机,进入调试模式,在Chrome中输入
chrome://inspect
然后选择自己的机型,点击具体的webview页面就能进入熟悉的前端调试页面啦
这个过程中我的魅族16p会显示404,换一台机子就行了 ,不要迷恋于一只机子。
还有一个比较好用的调试方法,就是代理映射,可以通过charles(or 其他)进行映射,因为很多时候js源码是被压缩过的很难看,这时我们可以通过映射原始版本来进行问题排查,方便很多。