How to kill an Android WebView…
Inhaltsverzeichnis
1. Preface Inhalt
Since this information will hopefully and ultimately prove to be useful to a lot of Android developers around the Planet I have decided to break with tradition again and write in English (or at least my humble attempt at it) again. In this small post I’d like to discuss several ways that aim at stopping or finishing a class of the Android operating systems that proves particularly difficult to kill: The WebView.
Said class is intended to render HTML-based content and can, if equipped with a proper WebViewClient, automatically follow references or render JavaScript based content. And exactly this feature or the lack thereof makes it a very interesting challenge.
2. Application Inhalt
Currently I work on x-platform applications that just need to visualize some given data and accept rudimentary user input; the applications aren’t very time critical or even require real-time responses, but must rather provide a similar Graphical User Interface (GUI) on each and every supported platform (like Phones, PC, Tablet, and so on).
Writing a native application in each of the platforms respective programming language (Objective-C, Java, Dot-Net) is obviously not feasible, rather an approach to write code just once and having to do only minor adaptions from platform to platform seems more appropriate. And furthermore what better way is there than to write our application in HTML and enliven it with JavaScript.
So, to get the app to the phone there are two ways: either create an HTML5 web package or code a tiny native application that merely offers a full screen sized web view and loads the application into this web view. I decided to use the latter, and since my App uses XHR/AJAX to communicate with my server every second, I didn’t see a need to use native socket connections or similar. In fact, I think WebSockets are quite unnecessary, but that is a different story.
3. The Problem Inhalt
Said way works like a charm. However, everytime I used my Application battery life was down to an hour or so, previously fully charged of course. Digging for reasons I quickly came to realise that even though I put the App in background, the XHR-JavaScript-requests continued to communicate unhindered oder Wi-Fi and GSM. Apparently the WebView does not stop any running JavaScript when put to sleep which offers new possibilities on one hand (consider using node.js on your phone…) but is highly undesired for my use case.
When I press the home button on an Android device for instance, I want the current App to come to a halt and not consume ressources more than neccesary. As intended by Google as I understand at least, by the way.
So, naturally, after blaming crappy garbage collecting and Java, I startet looking for ways to kill the Android WebView. Hence the basis for this article.
4. Solutions Inhalt
Let’s see how various solutions to pause or stop a WebView in Android work…
4.1 .getSettings().setJavaScriptEnabled() Inhalt
Well, you would think that disabling JavaScript in the WebView’s settings would have an instant effect, in short: it does not. Only after reloading the page would there be no javascript any more, and this is not nice by design, since the page is still more or less well rendered.
4.2 .stopLoading() Inhalt
Since XHR „loads“ something from another server, you might think that calling „webview.stopLoading()“ would have an effect. In short: it does not. Works only on ressources contained within the HTML-file. Pity, is it not… Well, maybe not, since there is no „startLoading()“ method to resume XHR after resuming the activity anyway.
4.3 .destroy() Inhalt
As a last resort one might think about „destroy()“ing that thing, and true enough, the WebView itself is not accessible after that. Its threads however continue to exist as zombies somewhere in the vast RAM space and also continue to send XHR requests…
4.4 .pauseTimers() / resumeTimers() Inhalt
In short: Nope, does not work. I even don’t know what these methods are good for if not for controlling JavaScript timers. There aren’t any in plain HTML, AFAIK.
Update: When it comes to timers only, these functions seem to work on 2.3.5 and upwards, however, when there is no timer active at the time of calling the function, all in vain. With my use case: When pausing the app while there is an XHR active (instead of the running timer that schedules the next XHR call), nothing happens and the next timer continues unhindered.
4.5 WebViews own onPause() and onResume() methods Inhalt
On StackOVerflow I found a similar question answered like this (see Google Groups #1 and #2 as well):
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:
// Copied from STOV
Class.forName("android.webkit.WebView")
.getMethod("onPause", (Class[]) null)
.invoke(webView, (Object[]) null);
In short again: Did not work for me on 2.3 to 3.2; maybe a particular Android version is required, I don’t know.
4.6 And finally: The Working One Inhalt
The only way I found to stop an XHR in the WebView was to save the URL into a temporary string, load a bogus asset-ressource and put the application to sleep after that. When resuming and the temporary URL contains a value, then load this value; it will be loaded from cache anyway (provided you let the cache handling be done with default settings) and not introduce any delays.
So here’s the source for the onPause():
// Code for onPause()
@Override
protected void onPause() {
super.onPause();
appWebViewTempUrl = appWebView.getUrl();
appWebView.loadUrl("file:///android_asset/infAppPaused.html");
}
…and for onResume() events:
// Code for onResume()
@Override
protected void onResume() {
super.onResume();
if (!appWebViewTempUrl.equals("") && appWebViewTempUrl != null) {
appWebView.loadUrl(appWebViewTempUrl);
}
}
5. Summary Inhalt
Of course, this way does not truely pause an XHR, it merely allows the Android Scheduler and the GC to work as intended. Whether or not the OS is to blame or it’s the programmer’s fault I don’t dare judge. Since the JavaScript however usually runs in a sandbox and cannot interact with the host system by design, I consider the inability to pause a webview the operating system’s fault, particularly, when there is no way to explicitly shut down („kill -9“) an application.
13 Kommentare zu “How to kill an Android WebView…”
I already figured out most of the logical solutions weren’t working before I found your site so it was nice to read the confirmation. As basic as your working solution is, it works. Thanks!
Thank you. Now i found solution.
But in my case, i just load the link onresume, because if you load new url even same url, it means you unload the previous link.
But with this, i loast my progreesbar whilie loading web. I can live with that.
Hi, thanks for the info.
I was loading a youtube video in a webview but couldn’t close it.
Now it’s working great.
Hi, thanks for sharing. I was wondering if I use the following code: appWebView.loadUrl(„about:blank“). Is it different to your solution? are they all used the cache page?
Greetings, thanks for your comment. I really don’t know. My guess is that the browser will load a blank page and in fact stop all previous javascript stuff, but the version of your built-in browser must support that. Loading an asset seems to be a safer way, as I don’t know how many browsers on 2.3 support it. Cheers
Where do you place the onPause() onResume() code?
Greetings, in my case (just a tiny app), I placed this into the main class‘ onPause/onResume code. hth, Cheers
Hi dude, i have also find out one good example
Show Loader To Open Url In WebView
Hi dude, i have also find out one good example
Show Loader To Open Url In WebView – Android Example
THANK YOU SO MUCH, THIS REALLY HELPED ME A LOT!
Woa! I have struggled with this for some days now, and finally found this page, explaining why my webView.OnPause() and webView.OnResume() did not work. Your solution is such a simple hack 🙂 Don’t you ever (re)move this blog post, as it is still relevant in 2018, for both native Android and Xamarin! -Greetings from around the world
This does not work on newer Android API 19 (95% of Android Devices).
Hi, thanks. I’m not sure what might be the problem. Even in API 19, there are both the getUrl() and loadUrl() methods available. So, in theory, the hack should still work. Perhaps it’s something different with your code? Best.
Einen Kommentar hinterlassen