38

Fail To Destroy WebView

Firstly, a lot of example i had been tried for destroy the webview in Android.

For example: Memory Leak in Android

Although i was destroying webview in the onDestroy() and declared a webview programmatically, but the memory leak problem also will be occurred in my Android device.

Below is my coding..

public class MainActivity 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.clearHistory();
    mWebView.clearCache(true);
    mWebView.clearView();
    mWebView.destroy();
    mWebView = null;        
}

Someone help me please.. thank you..

Community
  • 1
  • 1
user2477457
  • 383
  • 1
  • 3
  • 4

3 Answers3

67

The WebView might not be destroyed because you are removing the view in the onDestroy(), which can be called in a few different occasions: when the user exits the app via the back button, when the user presses the home button and then swipes the app from recents, or when the system kills your app to make room for other apps. It may be problematic to destroy the WebView in onDestroy().

Old Answer: To remove the WebView from memory, override the finish() method and place the code you have in onDestroy() in finish(). finish is called when the app is exited via the back button, so this will ensure that the WebView is destroyed.

New Answer: I was originally wrong about when the onDestroy method was called, so I and others have edited the question to remove the parts that were wrong. However, this also changes slightly what I would do to destroy the WebView. It may be that there is not enough time to destroy the WebView in onDestroy, or it could be that the Activity is getting destroyed multiple times, which would result in the crash, since the WebView would get destroyed multiple times and that would crash it (see the documentation quote at the bottom of this answer). The solution is to be more explicit when destroying the WebView and also to set the WebView to null so you can be sure that it wasn't destroyed before trying to destroy it.

When you use WebView.destroy(), internally the WebView will destroy itself, but the problem is that there is no way to determine if you've called destroy on an existing WebView object or not. This is problematic, because calling methods on the WebView after destroying it will result in a crash. The solution is to set the WebView to null after destroying it.

Your full code to kill the view should look like this (some things are optional):

public void destroyWebView() {

    // Make sure you remove the WebView from its parent view before doing anything.
    mWebContainer.removeAllViews();

    mWebView.clearHistory();

    // NOTE: clears RAM cache, if you pass true, it will also clear the disk cache.
    // Probably not a great idea to pass true if you have other WebViews still alive.
    mWebView.clearCache(true);

    // Loading a blank page is optional, but will ensure that the WebView isn't doing anything when you destroy it.
    mWebView.loadUrl("about:blank");

    mWebView.onPause();
    mWebView.removeAllViews();
    mWebView.destroyDrawingCache();

    // NOTE: This pauses JavaScript execution for ALL WebViews, 
    // do not use if you have other WebViews still alive. 
    // If you create another WebView after calling this, 
    // make sure to call mWebView.resumeTimers().
    mWebView.pauseTimers();

    // NOTE: This can occasionally cause a segfault below API 17 (4.2)
    mWebView.destroy();

    // Null out the reference so that you don't end up re-using it.
    mWebView = null;
}

This method should then be called somewhere, preferably in finish() since that will be explicitly called by the user, but it should work in onDestroy() as well.

NOTE: I should add that calling mWebView.onDestroy() twice may crash the browser. The docs state:

No other methods may be called on this WebView after destroy.

anthonycr
  • 4,146
  • 1
  • 28
  • 35
  • try adding mWebView.pauseTimers(); and mWebView.freeMemory() to the finish and onDestroy methods and see if that does anything (see above). Also, what API level are you testing this on? – anthonycr Jul 05 '13 at 15:16
  • clearView() should be changed to loadUrl("about:blank"), since clearView() is deprecated now. – Jon Willis Oct 15 '13 at 21:43
  • 2
    It is NOT true what you wrote about invoking onDestroy() method!!! It IS called when you exit the app via back button. And NOTHING is called when the app is killed (e.g. swiped from the recents screen). – Perry_ml Nov 11 '13 at 16:47
  • I am trying to clear webview from the Fragment.onDestroyView() and calling webview.destroy() even once without webview =null; crash the app with Singal 11. Any idea why? – Damian Petla Nov 12 '14 at 13:42
  • Why clearHistory() is called BEFORE calling loadUrl("about:blank")? I think stopLoading() -> loadUrl("about:blank") -> clearHistory() is better. And why you think mWebView = null is interpreted as mWebView.destroy()? (Ref count may be decresed but) WebView cannot know mWebView is set null. – Toris Jul 22 '15 at 12:16
  • @Toris I don't think it matters which order you call those methods in. setting the reference to the WebView to null means that the garbage collector will remove it from memory and will call its native destructor before garbage collecting it (I think). You can call destroy() and then immediately set the reference to null as well. – anthonycr Jul 22 '15 at 15:40
  • 4
    I used this code to cleanup after a webview used to play a YouTube video. Although the webview was cleaned up properly, whenever I tried returning to the same screen, the video would not render ever again. I removed the calls to mWebView.freeMemory(); (which has now been deprecated) and mWebView.pauseTimers(); and everything worked properly. – m_katsifarakis Apr 06 '16 at 14:18
  • 4
    I agree with @m_katsifarakis. pauseTimers() affects re-created webview. – satohiro May 17 '16 at 07:36
  • 2
    Good answer for the most part but also agree with @m_katsifarakis, freeMemory and pauseTimers only make things worse, I had problems until I removed them. – Jim109 Jan 10 '17 at 15:07
  • Answer has been updated, freeMemory is no longer needed since it has been deprecated since API 19, and pauseTimers should be used carefully as pointed out. I've commented parts of the code snippet to explain each part better. Note that if you're using pauseTimers, you need to call resumeTimers after creating a WebView. – anthonycr Jun 07 '17 at 21:48
  • `pauseTimers` applies to every `WebView` so you should only call it if you want all webviews to be paused, even ones with ads. – casolorz Sep 19 '19 at 15:53
  • This used to work but now the webview object seems to become null when removing it from its parent view – user1114 Jul 06 '21 at 02:52
2

In my case, When I kill the app and open it again. I click on the button to open webview, then app was crashing.

so, I have made changes in onDestroy() of that webview containing activity in the below way. Now my app is not crashing after doing this.

@Override
    protected void onDestroy() {
        super.onDestroy();
        webView.clearCache(true);
        webView.clearHistory();
        webView.onPause();
        webView.removeAllViews();
        webView.destroyDrawingCache();
        webView.pauseTimers();
        webView=null;
        super.onStop();
        finish();
    }

Hope it will help someone at some time if they have the same issue as me. Thanks to @anthonycr

2

There is documentation on this: https://developer.android.com/guide/webapps/managing-webview#termination-handle I have found the simplest way is to have the webview in the layout XML contained by a Layout that is also a ViewGroup.

Example snippets:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/layout_webview"
    >

    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</LinearLayout>


    webView = findViewById(R.id.webview)
    ...
    val webViewContainer: ViewGroup = findViewById(R.id.layout_webview)
    webViewContainer.removeView(webView)
    webView.destroy()
rwst
  • 2,515
  • 2
  • 30
  • 36