Memory leak in WebView

I have an activity using an xml layout where a WebView is embedded. I am not using the WebView in my activity code at all, all it does is sitting there in my xml layout and being visible.

Now, when I finish the activity, I find that my activity is not being cleared from memory. (I check via hprof dump). The activity is entirely cleared though if I remove the WebView from the xml layout.

I already tried a

webView.destroy();
webView = null;

in onDestroy() of my activity, but that doesn't help much.

In my hprof dump, my activity (named 'Browser') has the following remaining GC roots (after having called destroy() on it):

com.myapp.android.activity.browser.Browser
  - mContext of android.webkit.JWebCoreJavaBridge
    - sJavaBridge of android.webkit.BrowserFrame [Class]
  - mContext of android.webkit.PluginManager
    - mInstance of android.webkit.PluginManager [Class]  

I found that another developer has experienced similar thing, see the reply of Filipe Abrantes on: http://www.curious-creature.org/2008/12/18/avoid-memory-leaks-on-android/

Indeed a very interesting post. Recently I had a very hard time troubleshooting a memory leak on my Android app. In the end it turned out that my xml layout included a WebView component that, even if not used, was preventing the memory from being g-collected after screen rotations/app restart… is this a bug of the current implementation, or is there something specific that one needs to do when using WebViews

Now, unfortunately there has been no reply on the blog or the mailing list about this question yet. Therefore I am wondering, is that a bug in the SDK (maybe similar to the MapView bug as reported http://code.google.com/p/android/issues/detail?id=2181) or how to get the activity entirely off the memory with a webview embedded?


Solution 1:

I conclude from above comments and further tests, that the problem is a bug in the SDK: when creating a WebView via XML layout, the activity is passed as the context for the WebView, not the application context. When finishing the activity, the WebView still keeps references to the activity, therefore the activity doesn't get removed from the memory. I filed a bug report for that , see the link in the comment above.

webView = new WebView(getApplicationContext());

Note that this workaround only works for certain use cases, i.e. if you just need to display html in a webview, without any href-links nor links to dialogs, etc. See the comments below.

Solution 2:

I have had some luck with this method:

Put a FrameLayout in your xml as a container, lets call it web_container. Then programmatically ad the WebView as mentioned above. onDestroy, remove it from the FrameLayout.

Say this is somewhere in your xml layout file e.g. layout/your_layout.xml

<FrameLayout
    android:id="@+id/web_container"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"/>

Then after you inflate the view, add the WebView instantiated with the application context to your FrameLayout. onDestroy, call the webview's destroy method and remove it from the view hierarchy or you will leak.

public class TestActivity extends Activity {
    private FrameLayout mWebContainer;
    private WebView mWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.your_layout);

        mWebContainer = (FrameLayout) findViewById(R.id.web_container);
        mWebView = new WebView(getApplicationContext());
        mWebContainer.addView(mWebView);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mWebContainer.removeAllViews();
        mWebView.destroy();
    }
}

Also FrameLayout as well as the layout_width and layout_height were arbitrarily copied from an existing project where it works. I assume another ViewGroup would work and I am certain other layout dimensions will work.

This solution also works with RelativeLayout in place of FrameLayout.