WebView内存泄漏优化

1.首先不要在xml中去添加webView节点,而是在需要的时候动态生成。你可以在需要webview的布局位置放一个LinearLayout,需要时在代码中动态生成webview并add进去:

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

然后在onDestroy()时手动释放webView资源:

1
2
3
4
5
6
7
8
9
@Override
protected void onDestroy() {
if( mWebView!=null) {
mWebView.setVisibility(View.GONE);
mWebView.removeAllViews();
mWebView.destroy();
}
super.onDestroy();
}

  1. 手动删除引用
    这个方法在我的项目中没有效果,但原文博主说在他的项目中效果很好,也许对其他人的情况有效,在这里也记下来。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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中调用:

1
2
3
4
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setConfigCallback(WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE));
}

public void onDestroy() {
    setConfigCallback(null);
    super.onDestroy();
}

3.开启一个独立进程加载webView。这里会涉及到多进程通信的问题,相对会比较复杂,后续会单独写一篇关于独立进程加载webView的文章。

  1. 从根源解决(划重点)
    前面的方法都没有解决我内存泄漏的问题,然后我看到了一篇文章是从源码角度分析了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上面移除掉。

1
2
3
4
5
6
ViewParent parent = mWebView.getParent();
if (parent != null) {
((ViewGroup) parent).removeView(mWebView);
}

mWebView.destroy();

完整的activity的onDestroy()方法:
`
@Override
protected 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();

}