【Android】 WebView内存泄漏优化之路
阅读数:
这几年H5的快速发展,使得Hybrid混合开发越来越流行,而webview也成为了开发中必备的元素。但是我们知道WebView在加载页面时,会占用非常大的内存,无论是iOS还是Android系统上,加载一个Web页面一般需要用到100M左右,而如果我们不及时清理WebView的内存,那最后可能会随着内存消耗的不断增加而发生OOM(Out Of Memory)导致程序崩溃。那今天我就总结一下前一阵子在开发中解决WebView内存消耗过大问题吧,当然,这个过程也是参考了很多前人的经验总结,我会照例在文章的末尾列出来表示感谢。
这里先交代下关于查看程序的内存消耗,笔者每次都是在程序运行起来以后,通过手机上的系统工具来查看每个应用实时消耗内存的大小。因为AndroidStudio的Monitor中的内存消耗并没有记录WebView,而手机上工具显示的一定是准确有效的,所以,不要说我看AndroidStudio里面的内存并没有增加啊就误以为你的内存控制策略真的有效。
好了,我们来看一下一个有效的WebView内存控制策略是如何的。
用代码New一个WebView而不是在XML中静态写入
这个步骤是之后所有步骤的前提,如果你的WebView是在XML中静态存在的,那么本篇博文后面的方法将不会起到效果。
那么如何通过代码New一个WebView呢?
在XML文件中用layout占位
我们可以通过建立一个layout(无论是RelativeLayout亦或是LinearLayout)来作为WebView的容器(Container)。
一个简单的XML代码片段如下:
1 | <!-- webViewContainer--> |
在对应的Activity中通过代码添加
接下来只需要在Activity中New一个WebView并且添加到我们的容器中就ok了。
同样奉上代码片段:
1 | RelativeLayout webview_container = (RelativeLayout) findViewById(R.id.base_web_view_container); |
这里特别说明几点:
关于WebView的context应该用Activity还是Application的context,这里网上较为一致的观点都是采用Application的,理由是这样不会造成Activity的context的内存泄漏。
不过我用LearCanary检测并没有发现因为WebView引用了Activity的context而导致Activity的内存泄漏。
而网上说的引用Application的context会在WebView的某些特殊动作是产生由Application到Activity的类型转换错误,关于这个错误我也并没有遇到过,而我的WebView也是负责了相当多的操作,例如视频播放,弹窗处理等等,所以,不要一味地听信别人的结论,一定要实实在在地动手写一遍。
但是我这里依旧使用的是Application的Context,因为经过我的实际测试,在反复New和销毁WebView时,采用Application要比采用Activity的context要稳定地少用20~30M左右的内存,虽然他们的内存都维持在一个稳定的消耗水平,但总体看来,Application要比Activity少那么一点。
销毁时的动作
当我们使用完了一个WebView,要销毁时,需要做的事情其实也很简单,我们直接来看代码吧:
1 | /** |
在你要退出Activity或者单纯地想要销毁一个webview时,调用这个方法就好了。
大家一定有注意到我的代码中有一行代码前面加了很多的注释。这里面其实是一段辛酸的故事:
笔者的程序在大部分机型上都测试的没有问题,但是唯独在小米的几个机型(小米4和红米)上,总是无缘无故出现内存泄漏而导致闪退,最后定位确实是因为WebView的某些原因导致的,但是又苦于找不到是什么原因,因为在别的手机上无法重现,唯独小米的手机上,在WebView之间切换时,不定期就会闪退。
但是即便在程序中安装了LeakCanary,也没有检测到哪里发生了内存泄漏。
直到第二天,我在其他手机上运行时,突然检测到一个mComponentCallbacks
导致的内存泄漏,于是就在google上搜到了这篇文章,里面提到是由于Android5.1中的一个bug引起的新的内存泄漏点,正好我的手机也是5.1系统的,于是就出现了这个内存泄漏,于是我就按照帖子里给出方法,由webview的父view手动把它remove出去。
结果,这一行简单的代码((ViewGroup) web_view_.getParent()).removeView(web_view_);
竟然顺便修好了小米手机上闪退的问题,虽然那几台小米手机都不是Android5.1版本的,但是问题就这么随之修复了。。。
好了,这就是在我的工程中切实有效的优化WebView的所有代码了。当然,我也在网上找到了微信开创的单独启用一个线程来进行webview的相关操作,然后退出时直接杀死这个线程来解决webview的内存泄漏的问题,但是和我的工程不太相符,因为这种方法要求你的webview在单独的Activity中,这样你可以在finish activity时杀死进程;而我的程序是因为在一个activity中要显示多个webview,所以,那种方法显然不好使了。
总之,选择何种解法还是要根据你自己的工程来的。
还有就是,LearCanary确实是个很好用的工具,强烈推荐大家使用。
最后奉上参考文章: