3

I'm working in an Android WebApp using Phonegap. To exit the application, I'm calling navigator.app.exitApp from JavaScript code.

I tried to find the documentation page for this method, but it does not exist. Then I downloaded the Phonegap source code for Android, but I got lost without finding where and how does this library exit the app from native code. I've no time for this so I decided to test it on my own.

My app has a single DroidGap activity (launched with singleInstance flag), but also a Service and a Broadcast Receiver. The user must be able to close the GUI, but I'd like to keep the services and everything else running. So what I'd like to do is to finish only the activity, instead of calling System.exit.

Now here comes the odd part. I've tested this in a simulator and the DroidGap activity seems to close the way I want it to (onDestroy is fired) and the services are kept running. But, despite the activity is closed, and the app icon is no longer shown in the task manager, somehow a JavaScript timer is still running. I've triple-checked this. I'd like to believe the activity is in the background, but it can't be: it is launched as singleInstance, and when I open the app again it shows the initial page (it was closed in a different page).

Why does this happens? Should I code a plugin to directly call finish over the activity?

Thanks in advance.

Mister Smith
  • 27,417
  • 21
  • 110
  • 193
  • Just to share, I experience the same thing. Somehow eventhough the app is closed and if we have some timer (using setInterval for example), sometimes it will keep running. My theory is that the app is destroyed but the webview is somehow remained in memory got executed? – wmfairuz Apr 09 '13 at 11:15
  • I'm currently debugging it on real device. There's a `WebViewCoreThread` that is still running after closing the activity. I've found this question, probably related: http://stackoverflow.com/q/2040963/813951 – Mister Smith Apr 09 '13 at 11:23
  • That's weird. How can the webview still alive after the app is destroyed? – wmfairuz Apr 09 '13 at 11:34
  • 1
    The app process is not destroyed because there are more components running. The WebView and the Activity are destroyed, but the thread associated to the WebView is still running (same as when you launch a new thread in a regular activity and you finish the activity) – Mister Smith Apr 09 '13 at 11:53
  • It seems you answer your own question then :) – wmfairuz Apr 09 '13 at 11:56

2 Answers2

2

I've been investigating this for a couple hours, so I'll post here my findings.

In every Android WebApp, not only Phonegap apps, whenever you have a WebView two threads are spawned: WebViewCoreThread and WebViewWorkerThread. They are shown in DDMS. I've been reading the Android sources and I've tracked the creation of this thread down to the WebViewCore.java file. Apparently only two threads per application process are started, and they are shared by every WebView in the app.

IMHO, the fact that using a View in a screen means spawning threads is very bad design. If this field needs to do things as complex as starting a thread, then it should have been better designed as WebViewActivity or WebWiewFragment instead. In any case, the class that creates the thread should also be responsible to finish it when it is no longer needed. This unfortunately is not the case.

When the activity containing the WebView is closed but the application process is still running (maybe because you have other components like services, or perhaps the OS has not killed it yet), these threads are kept running. The core thread will continue executing the timers you set in your app. When the activity is created again, the threads are reused.

As a side note, notice how this allows running javascript code in background without having to keep an activity displayed.

I tried some of the workarounds for a regular WevView exposed in this question: WebView threads never stop (WebViewCoreThread, CookieSyncManager, http[0-3]),

but for some unknown reason it does not work with Phonegap. I tried overriding my DroidGap subclass like this:

@Override
public void onDestroy(){
    System.out.println("Activity destroyed");

    if(appView != null){ //This is a protected reference to the `CordovaWebView`, extending `WebView`
        //First attempt
        try {
            Class.forName("android.webkit.WebView").getMethod("onPause", (Class[]) null).invoke(appView, (Object[]) null);
        } catch (Exception e) {             
            e.printStackTrace();
        } 

        //Second attempt
        appView.pauseTimers();          
    }

    super.onDestroy();
}

So what I finally did is to make sure all timers are cancelled from JavaScript code before exiting the app. However, one would expect this fixed in the Android OS code, or patched in Phonegap, because even in the best case these threads are wasting resources that really are not needed when there's no web page being shown. And in the worst scenario, some DOM related JavaScript code could crash.

Community
  • 1
  • 1
Mister Smith
  • 27,417
  • 21
  • 110
  • 193
  • Thanks for sharing. How did you manage to cancel all timers? I have the problem that my WebView is send to background by some users and is being killed by the Android system. I see no possibility to cancel all timers manually... – ottel142 Sep 13 '13 at 17:26
  • @ottel142 I'd start searching `setInterval` through the javascript files, and making sure each call result is stored in a variable, so that you can cancel them later with `clearInterval`. Same with `setTimeout` calls. Another possibility is to call `System.exit` or `Process.killProcess` in your activity's `onDestroy`, that should kill the entire main process. In case you needed a service, you might want to launch it in a different process, so that it does not get killed along with the activity. – Mister Smith Sep 16 '13 at 09:07
0

You need to kill the timer explicitly, right before calling navigator.app.exitApp(). This issue is also discussed here.

Hope this helps...:)

Community
  • 1
  • 1
umair.ali
  • 2,714
  • 2
  • 20
  • 30