36

I use a WebView to display some internet content on one of our app's Activities.
The problem is that when the user switches out of this activity, WebView's threads keep running!
The problematic threads are:

Thread [<17> WebViewCoreThread] (Running)
Thread [<25> CookieSyncManager] (Running)
Thread [<19> http0] (Running)
Thread [<29> http1] (Running)
Thread [<31> http2] (Running)
Thread [<33> http3] (Running)

Pausing each one of these threads, and checking what it is busy doing:

Thread [<17> WebViewCoreThread] (Suspended)
    Object.wait(long, int) line: not available [native method]
    MessageQueue(Object).wait() line: 288
    MessageQueue.next() line: 148
    Looper.loop() line: 110
    WebViewCore$WebCoreThread.run() line: 471
    Thread.run() line: 1060

Thread [<25> CookieSyncManager] (Suspended)
    Object.wait(long, int) line: not available [native method]
    MessageQueue(Object).wait(long) line: 326
    MessageQueue.next() line: 144
    Looper.loop() line: 110
    CookieSyncManager(WebSyncManager).run() line: 90
    Thread.run() line: 1060

Thread [<19> http0] (Suspended)
    Object.wait(long, int) line: not available [native method]
    RequestQueue(Object).wait() line: 288
    ConnectionThread.run() line: 93

I wonder how can I tell the Looper in each of those threads to quit.

I tried calling webView.destroy() in the activity's onPause() method, but it had no influence.
When I disable the call for opening a web page in the webView ( webView.loadUrl(...) ), those threads naturally are not started, and therefore don't stay on after leaving the activity.

Any ideas as to how I can make WebView's threads stop after leaving their activity?

Volo
  • 28,673
  • 12
  • 97
  • 125
Amit Kotlovski
  • 1,848
  • 1
  • 18
  • 13
  • "Any ideas as to how I can make WebView's threads stop after leaving their activity?" Why do you care? What specific harm is it causing? – CommonsWare Jan 11 '10 at 13:09
  • 4
    I have been trying to avoid having extra threads running, holding resources in memory and (possibly) adding more cpu scheduling slots. Are you saying it will have no influence on performance nor resources? – Amit Kotlovski Jan 13 '10 at 08:53
  • 5
    same here. WebView threads drains battery too much. – xrath Jun 13 '10 at 12:58

10 Answers10

40

You should be able to stop / resume these threads by calling the onPause / onResume on the webview.

Those are however hidden, so you will need to do it through reflection. The following code worked for me:

Class.forName("android.webkit.WebView").getMethod("onPause", (Class[]) null).invoke(webView, (Object[]) null);

Where webView is the instance of WebView.

Also see: http://code.google.com/p/android/issues/detail?id=10282

Skymt
  • 1,089
  • 9
  • 10
  • Thanks for you answer. As the bug report in the url you provided says, it might work, but it "feels a little dirty". I guess for now it is the best answer to this question. – Amit Kotlovski Aug 25 '10 at 11:16
  • Could you be more clear please as to where I have to add this code. – user1106888 Apr 17 '13 at 20:52
  • Well, taking the original question into account, I would override the main activity's methods onPause and onResume and call it from there. – Skymt Apr 24 '13 at 10:07
  • 3
    Please note that this does not work if the WebView has a Javascript timer of some kind running. – mkuech Jun 11 '13 at 19:18
16

My solution, in extended webview class(tested in android 2.2, andriod 2.3, android 3.1):

 private boolean is_gone = false;
 public void onWindowVisibilityChanged(int visibility) {
     super.onWindowVisibilityChanged(visibility);
     if (visibility == View.GONE) {
         try {
             WebView.class.getMethod("onPause").invoke(this); //stop flash
         } catch (Exception e) {}
         this.pauseTimers();
         this.is_gone = true;
     } else if (visibility == View.VISIBLE) {
         try {
             WebView.class.getMethod("onResume").invoke(this); //resume flash
         } catch (Exception e) {}
         this.resumeTimers();
         this.is_gone = false;
     }
 }
 public void onDetachedFromWindow() { //this will be trigger when back key pressed, not when home key pressed
     if (this.is_gone) {
         try {
             this.destroy();
         } catch (Exception e) {}
     }
 }
Murat Karagöz
  • 35,401
  • 16
  • 78
  • 107
diyism
  • 12,477
  • 5
  • 46
  • 46
  • Works great! it actually made my app much faster. – Rotemmiz Apr 25 '12 at 12:23
  • A caveat to keep in mind. I noticed that if you call this.pauseTimers(), all WebView's are being paused, not just the instance your calling this on. This was on Adroid 4.0.4 – Geert Weening Jul 23 '12 at 13:34
  • Thanks for Weenings's testing in Andrdoid 4.0.4 – diyism Jul 27 '12 at 03:46
  • 1
    This is working. Let me repeat: THIS, IS, WORKING. Other workaround weren't good enough, because if you had a webview in a fragment, if you lost attachment, you didn't have access to the thread anymore. But this is working flawlessly. Thank you so much. – Reinherd Mar 19 '14 at 16:49
  • 1
    Bedst solution by far :) – JWqvist Mar 31 '14 at 10:45
13

A clean easy solution that does the job:

@Override
public void onPause() {
    super.onPause();
    webView.onPause();
    webView.pauseTimers(); //careful with this! Pauses all layout, parsing, and JavaScript timers for all WebViews.
}

@Override
public void onResume() {
    super.onResume();
    webView.onResume();
    webView.resumeTimers();
}

@Override
public void onDestroy() {
    super.onDestroy();
    parentViewGroup.removeAllViews(); //the viewgroup the webview is attached to
    webView.loadUrl("about:blank"); 
    webView.stopLoading();
    webView.setWebChromeClient(null);
    webView.setWebViewClient(null);
    webView.destroy();
    webView = null;
}
Bolling
  • 3,954
  • 1
  • 27
  • 29
  • onPause and onResume of webview weren't accessible prior to API 11. – Ayyappa Mar 12 '15 at 22:04
  • This worked excellent for my 4.4.2 application, though i havnt yet figured out what "parentViewGroup.removeAllViews(); " is for, but "javaScriptTester.removeAllViews();" seemed to be accepted by the ide. so?? and like Ayyappa said it requires api level 11. – Logic1 Jun 23 '15 at 08:27
  • 2
    @GmanO removeAllViews() cleans up any associated views such as the built in zoom controls. If they're displayed when you back out of the activity, you'll get a leak. See https://stackoverflow.com/questions/27254570/android-view-windowleaked-activity-has-leaked-window-android-widget-zoombuttons – Scott D. Strader Jun 29 '15 at 19:51
4

Take a look at this andev.org thread (WebViewCoreThread problem).

See the response... Re: WebViewCoreThread problem - Postby potatoho

2) To pause flash you have to call hidden methods WebView.onPause() / WebView.onResume().

3) To pause WebViewCoreThread you use WebView.pauseTimers() / WebView.resumeTimers().

I skipped the onPause/onResume, but I implemented webView.pauseTimers() and webView.resumeTimers() and it at least stops the CPU bleed.

I also added CookieSyncManager.getInstance().stopSync() / startSync to my onPause/onResume as well.

Simley Jo
  • 33
  • 8
rquinn
  • 398
  • 3
  • 8
  • 2
    Thanks, i found solution in the blog:In your Activity onPause you should call WebView.onPause() and WebView.pauseTimers(). In your Activity onResume you should call WebView.onResume() and WebView.resumeTimers(). But since there is a one-off error in the refcount, you need to call WebView.resumeTimers() when you first create the WebView. WebView.class.getMethod("onPause").invoke(webview); – diyism Sep 26 '11 at 01:44
2

What about WebView.destroy()? That seems to be cleaning things up or me without doing any funky reflection calls.

2

Actually, using webView.destroy(), onPause(), clear**() cannot stop Webcore and its related threads yet.

Practically, an ideal way is to set the activity where WebView resides to be in an independent process, so that when the activity is finished, all the threads including Webcore will be destroyed.

That is the way I have employed. Maybe it is also useful to you.

Verdigrass
  • 1,203
  • 12
  • 14
2

Thanks for these info, I had this problem pausing sounds in my swf on webView when I press the lock button on the phone, onPause and onResume still plays my swf when i press the unlock button but the activity is still not visible, I suggest you place these on onWindowFocusChanged if you want to pause and resume playing of swf sounds in webView

@Override
public void onWindowFocusChanged(boolean hasFocus) {
gameView = (WebView) findViewById(R.id.gameView);
    // TODO Auto-generated method stub
    if (hasFocus) {
        try {
            Class.forName("android.webkit.WebView")
                    .getMethod("onResume", (Class[]) null)
                    .invoke(gameView, (Object[]) null);
        } catch (Exception e) {

        }
        gameView.resumeTimers();
        gameView.loadUrl("javascript:setflashfocus()");
    } else {
        try {
            Class.forName("android.webkit.WebView")
                    .getMethod("onPause", (Class[]) null)
                    .invoke(gameView, (Object[]) null);
        } catch (Exception e) {

        }
        gameView.pauseTimers();
    }
    super.onWindowFocusChanged(hasFocus);
}
0

The above solutions failed for me on Samsung Galaxy S with Android 2.3.3. But I made success trying the below workaround:

    webview.loadUrl("file:///android_asset/nonexistent.html");

I am forcing the webview to load a non existent html file. This seems to be forcing the webview to clean things up.

dangel
  • 1,506
  • 15
  • 10
0

Define a method:

    private void hiddenWebViewMethod(String state){
        if( webView != null ){
            try {
                Method method = WebView.class.getMethod(state);
                method.invoke(webView);
            } catch (Exception e) {
                Log.e("TAG", "Exception: " + e.toString());
                webView.loadData("", "text/html", "utf-8");
            }
        }
    }

then call hiddenWebViewMethod("onPause"); to stop loading, and hiddenWebViewMethod("onResume"); to resume.

Ayaz Alifov
  • 8,334
  • 4
  • 61
  • 56
0

I had to call onDestroy of the WebView to clear the memory specifically for the webview. I was loading flash in and it was killing my app when we go back to this activity with a webview. Flash will eat you alive if you don't destroy your WebView EVERY TIME you're done with it.

Eric Novins
  • 431
  • 3
  • 15