WebView造成的内存泄露

今天在检测内存泄露的时候,发现有一个activity的泄露是这样的:

Browser是继承自Application的类,在自己的这个类里面看了下,没有mComponentCallbacks这个成员变量,那么猜想可能是在父类Application中,看了下源码,结果真的是

这个类里面还有注册和反注册:

从上面内存泄露的调用栈来看,就是application里的成员变量,持有了一个activity实例,而这个成员变量,实际上就关联到了webview的实例,这个成员变量有注册和反注册功能,也就是说我们可能在某些地方没有进行反注册。

那么注册和反注册是在什么地方调用的呢?

这就要看这个类了org.chromium.android_webview.AwContents  

在5.1上(实际上是4.4就开始了http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1106/1921.html),内核中的webview实际上已经从webkit变更为chrome的了。(但是实际上对开发者来说用的还是webview这个接口,在源码的位置是:frameworks/base/core/java/android/webkit,webview的实际功能交给WebViewProvider来做,而新的AwContents这个类是在:frameworks/webview/chromium/java/com/android/webview/chromium/)

也就是上面内存泄露调用栈中的AwContents。

这个类的两个方法

看看这两个方法 onAttachedToWindow 和 onDetachedFromWindow:

	@Override
    public void onAttachedToWindow() {
        if (isDestroyed()) return;
        if (mIsAttachedToWindow) {
            Log.w(TAG, "onAttachedToWindow called when already attached. Ignoring");
            return;
        }
        mIsAttachedToWindow = true;

        mContentViewCore.onAttachedToWindow();
        nativeOnAttachedToWindow(mNativeAwContents, mContainerView.getWidth(),
                mContainerView.getHeight());
        updateHardwareAcceleratedFeaturesToggle();

        if (mComponentCallbacks != null) return;
        mComponentCallbacks = new AwComponentCallbacks();
        mContext.registerComponentCallbacks(mComponentCallbacks);
    }

    @Override
    public void onDetachedFromWindow() {
        if (isDestroyed()) return;
        if (!mIsAttachedToWindow) {
            Log.w(TAG, "onDetachedFromWindow called when already detached. Ignoring");
            return;
        }
        mIsAttachedToWindow = false;
        hideAutofillPopup();
        nativeOnDetachedFromWindow(mNativeAwContents);

        mContentViewCore.onDetachedFromWindow();
        updateHardwareAcceleratedFeaturesToggle();

        if (mComponentCallbacks != null) {
            mContext.unregisterComponentCallbacks(mComponentCallbacks);
            mComponentCallbacks = null;
        }

        mScrollAccessibilityHelper.removePostedCallbacks();
    }

调用了mContext的注册和反注册方法。实际上context的registerComponentCallbacks 方法是在基类Context中实现的,它具体的是调用了Application的registerComponent方法:

在上面的onDetachedFromWindow 中,一旦我们这个if (isDestroyed()) return; 检测为true,就返回了不会去执行反注册方法。

而这个isDestroyed又是在我们执行webview的destroy的时候被赋值的。所以如果我们在webview的onDetachedFromWindow前先执行了webview的destroy方法, 那么就可能存在泄露。所以正确的做法就是:

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

	mWebView.destroy();

完整的代码如下:

	public void destroy() {
        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();

            try {
                mWebView.destroy();
            } catch (Throwable ex) {

            }
        }
    }

,ps:其实我们在开发过程中,使用的是sdk的提供的WebView这个类,这个类是

package android.webkit;

包下的,  我们在这个类里面没有看到相关的onDetachedFromWindow方法。

在高版本的系统中,我们依旧还是使用WebView这个api,但实际系统为我们关联到了AwContents这个类了,实际上真正执行的是这个类里的方法。

最后,附上完整的分析文章链接:

Android 5.1 WebView内存泄漏分析

猜你喜欢

转载自blog.csdn.net/anhenzhufeng/article/details/80312134