WebView内存泄漏--解决方法小结

2018-11-22 21:20:04

有些东西还是记下来比较好,记记随笔,欢迎批评建议。


前段时间在项目中就用到webview展示大量的新闻资讯页面,然后就惊喜的出现内存泄漏了,于是乎我在网上查了一些资料然后在这里总结一下解决方法,欢迎拍砖。
(方法4划重点)。
Android混合开发时经常用到WebView加载html等页面,而WebView的内存泄漏就是最经常遇到的问题,尤其是当项目中需要用webview加载的页面比较多时。

即使当我退出页面时在我的BrowserActivity的onDestroy()方法中进行内存占用回收(如下图)但并没有效果:

mWebView.removeAllViews();
mWebView.destroy();
mWebView=null;

当我点开了多少条新闻内存中就存在多少个BrowserActivity的实例,说明我退出时这个BrowserActivity没有被回收,这样的话当我浏览的新闻比较多时,内存就会累积存在一定的OOM风险,而且新闻界面一般存在大量图片,所以这个问题是必须要解决的。

1. new一个而不是在.xml中定义webview节点

attention:最初在写这篇的时候这一小节可能写的不够严谨,要是造成误解真是抱歉;写这篇小结时的目的也是想把知道的一些解决方法记下来方便自己查看,没想到能收到评论和质疑,我还是很开心的,但是通过这个我也发现,发出来的东西还是要写的严谨一些,会慢慢改进的。所以这一小节重新说明了一下,要是有不对的地方还是欢迎大家拍砖。

不要在布局文件中定义webview的节点,而是在需要的时候动态生成。你可以在需要webview的布局位置放一个LinearLayout,需要时在代码中动态生成webview并add进去:

//mWebView=new WebView(this);mWebView=new WebView(getApplicationContext());
LinearLayout linearLayout  = findViewById(R.id.xxx);
linearLayout.addView(mWebView);

然后在onDestroy()方法中调用:

@Overrideprotected void onDestroy() {    if( mWebView!=null) {
       mWebView.setVisibility(View.GONE);
       mWebView.removeAllViews();
       mWebView.destroy();
    }    super.onDestroy();
}

tips: 关于创建webview时new WebView(...);到底是传入ApplicationContext还是Activity的context,说法不一,但是网上较为一致的观点是采用application的context。
传ApplicationContext貌似可以防止webview对activity的引用而造成的内存泄漏;但是在很多情况下会报错,但是这个出错应该是webview的某些特殊动作产生由Application到Activity的类型转换错误;
采用activity的context细想来貌似和在xml中直接定义没有什么区别;

2. 手动删除引用

这个方法在我的项目中没有效果,但原文博主说在他的项目中效果很好,也许对其他人的情况有效,在这里也记下来。

public void setConfigCallback(WindowManager windowManager) {    try {
        Field field = WebView.class.getDeclaredField("mWebViewCore");
        field = field.getType().getDeclaredField("mBrowserFrame");
        field = field.getType().getDeclaredField("sConfigCallback");
        field.setAccessible(true);
        Object configCallback = field.get(null);        if (null == configCallback) {            return;
        }

        field = field.getType().getDeclaredField("mWindowManager");
        field.setAccessible(true);
        field.set(configCallback, windowManager);
    } catch(Exception e) {
    }
}

然后在activity中调用:

   public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);
        setConfigCallback(WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE));
    }    public void onDestroy() {
        setConfigCallback(null);        super.onDestroy();
    }

3. 进程

为加载WebView的界面开启新进程,在该页面退出之后关闭这个进程。
这个方法我没有测试,不知道应用和效果如何,有兴趣的可以试试。

4. 从根源解决(划重点)

前面的方法都没有解决我内存泄漏的问题,然后我看到了一篇文章是从源码角度分析了webview内存泄漏的原因,最后按作者的方法解决了问题,后面会贴上原文地址。这里简单说一下:
原文里说的webview引起的内存泄漏主要是因为org.chromium.android_webview.AwContents 类中注册了component callbacks,但是未正常反注册而导致的。

org.chromium.android_webview.AwContents 类中有这两个方法 onAttachedToWindow 和 onDetachedFromWindow;系统会在attach和detach处进行注册和反注册component callback;
在onDetachedFromWindow() 方法的第一行中:

if (isDestroyed()) return;,

如果 isDestroyed() 返回 true 的话,那么后续的逻辑就不能正常走到,所以就不会执行unregister的操作;我们的activity退出的时候,都会主动调用 WebView.destroy() 方法,这会导致 isDestroyed() 返回 true;destroy()的执行时间又在onDetachedFromWindow之前,所以就会导致不能正常进行unregister()。
然后解决方法就是:让onDetachedFromWindow先走,在主动调用destroy()之前,把webview从它的parent上面移除掉。

ViewParent parent = mWebView.getParent();if (parent != null) {
    ((ViewGroup) parent).removeView(mWebView);
}

mWebView.destroy();

完整的activity的onDestroy()方法:

@Overrideprotected void onDestroy() {    if( mWebView!=null) {        // 如果先调用destroy()方法,则会命中if (isDestroyed()) return;这一行代码,需要先onDetachedFromWindow(),再
        // destory()
        ViewParent parent = mWebView.getParent();        if (parent != null) {
            ((ViewGroup) parent).removeView(mWebView);
        }

        mWebView.stopLoading();        // 退出时调用此方法,移除绑定的服务,否则某些特定系统会报错
        mWebView.getSettings().setJavaScriptEnabled(false);
        mWebView.clearHistory();
        mWebView.clearView();
        mWebView.removeAllViews();
        mWebView.destroy();

    }    super.on Destroy();
}

这个方法亲测有效。



  • 2020-03-18 21:15:34

    使用canvas画布解决百度地图自定义图层全球连续显示问题

        基于百度地图的Web API进行自定义图层叠加时,默认的图层只能叠加到全球范围以内,即经度范围为[-180, 180],而无法将图层叠加到默认的全球范围以外,即经度范围超出了[-180, 180]之后,经纬度坐标会自动回归到(0, 0),而导致在地图拖拽时全球以外无法连续显示想要的图层,此时可以基于百度地图的自定义图层将经纬度坐标转为像素点使用画布canvas来解决该问题。解决后效果如下图所示: ———————————————— 版权声明:本文为CSDN博主「宏伟杰作」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/u011284073/article/details/80549950

  • 2020-03-18 21:18:01

    node-canvas实现百度地图个性化底图绘制

    随着nodejs的推出,node的并发和异步的强大能力,越来越多的得到应用,而且取得了非常不错的效果。 作为一个前端工程师对node.js自然有着一份更深的感情,跃跃欲试的心情,总希望能将它应用到产品中来。

  • 2020-03-18 21:19:28

    高德地图和canvas画图结合应用的一些感想(一)

    入了团队才发现,该项目前后端分离,后端工程师已就位主要实现接口,IOS端工程师也已就位,还差一个web前端工程师。背脊一凉,我之前虽然写过一些js和css,虽有点功底但是离前端工程师还是有距离的啊。在和朋友说明情况后,朋友也是胆大,让我试试,主要他实在找不到人了(也有可能目前前端工程师报价都太贵了,创业嘛,能节约就节约,能理解。。。),没办法,走一步算一步吧。

  • 2020-03-18 21:30:57

    基于OpenLayers实现地理围栏 谷歌百度高德地图都可以

    前言.因为项目有点特殊,需要接入谷歌地图实现地理围栏,因为谷歌地图的地理围栏接口相关接口并不完善,于是就换了一个思路,利用OpenLayers来实现地理围栏 openlayers 中文地址 http://weilin.me/ol3-primer/ch02/index.html 作者:zcty0701 链接:https://www.jianshu.com/p/60e88ee1e843 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 2020-03-19 17:12:40

    百度地图放大覆盖物消失

    产生问题的原因是因为我们用的普通的点数组生成的多边形,我们应该用百度的点数组生成就没问题了。