1. 程式人生 > >WebView造成的記憶體洩露

WebView造成的記憶體洩露

今天在檢測記憶體洩露的時候,發現有一個activity的洩露是這樣的:


Browser是繼承自Application的類,在自己的這個類裡面看了下,沒有mComponentCallbacks這個成員變數,那麼猜想可能是在父類Application中,看了下原始碼,結果真的是


這個類裡面還有註冊和反註冊:


從上面記憶體洩露的呼叫棧來看,就是application裡的成員變數,持有了一個activity例項,而這個成員變數,實際上就關聯到了webview的例項,這個成員變數有註冊和反註冊功能,也就是說我們可能在某些地方沒有進行反註冊。

那麼註冊和反註冊是在什麼地方呼叫的呢?

這就要看這個類了org.chromium.android_webview.AwContents

  

在5.1上,核心中的webview實際上已經從webkit變更為chrome的了。也就是上面記憶體洩露呼叫棧中的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這個類了,實際上真正執行的是這個類裡的方法。

最後,附上完整的分析文章連結: