2

I am currently developing an Application for Android. One of the requirements is extensive logging about how the application is used. More specifically there should be logging about when the user closes the app. This logging consists of a server interaction. With respect to that specific requirement I stumbled onto:

Detect Application Exit(1) and Detect application Exit (2)

Both questions have an accepted answer relying on Service#onTaskRemoved(Intent).

In my case however this solution does not seem to work, i.e. AsyncTasks that are started in this method are only occasionally executed. More specifically, the onPreExecute is executed always but the doInBackground is not. I tested this on a Nexus 5 with Android 6 (Marshmallow) installed.

public class SomeService extends Service {

  @Override
  public IBinder onBind( Intent aIntent ) {
    return null;
  }

  @Override
  public void onTaskRemoved(Intent aRootIntent ) {
    new DoSomethingTask().executeOnExecutor( Asyntask.THREAD_POOL_EXECUTOR );
  }

  private static final class DoSomethingTask extends AsyncTask<Void, Void, Void> {

    @Override 
    protected void onPreExecute() {
      Log.e( DoSomethingTask.class.getName(), "This is executed always");
    }

    @Override
    protected Void doInBackground( Void... aParams ) {
      Log.e( DoSomethingTask.class.getName(), "This appears to be executed only sometimes... ");
      // here an actual call to a Rest API should be made to inform the server that the user has closed the application.
    }

    @Override
    protected void onCancelled( Void result ) {
      super.onCancelled( result );
      Log.e( DoSomethingTask.class.getName(), "Never invoked" );
    }

    @Override
    protected void onCancelled() {
      super.onCancelled();
      Log.e( DoSomethingTask.class.getName(), "Never invoked" );
    }
  }
}

Here is an overview of everything I tried in addition to the above code sample:

  • I have tried various onStartCommand options (START_STICKY, START_NOT_STICKY, etc.) without success.
  • I have also tried restarting the service in the onTaskRemoved method and then executing the AsyncTask in the onStartCommand.
  • Starting an IntentService in the onTaskRemoved method (which starts the AsyncTask in its onHandleIntent method) solves the problem neither.
  • using a BroadcastReceiver in combination with a local broadcast (LocalBroadcastManager#sendBroadcast) does also not work (I double checked that the broadcast receiver is effectively registered as receiver for the sent broadcast).

EDIT:

I have also taken a look at the callbacks in the Application class: - onTerminate : this method is only invoked in emulated environments and hence useless - onTrimMemory(int) : this method can be used for detecting when the app goes to the background, but it has no distinct case for when the app exits.

I could keep an activity stack (which would be updated in Activity#onPause(), etc.). But this requires quite a lot of work in every single Activity instead of the above Service approach which only involves interference at a single place.

Community
  • 1
  • 1
Kristof P.
  • 117
  • 1
  • 1
  • 7
  • You might be able to do this by extending the Application class and overriding onTerminate() onDestroy() onLowMemory() and things like that. – napkinsterror Nov 12 '15 at 23:27
  • I have tried that as well, but I forgot to mention it in the post. I have done that now at the bottom, see the **Edit** section. – Kristof P. Nov 13 '15 at 06:56

1 Answers1

0

First of all: in Android you cannot guarantee execution for your requirement. Period. The system is free to gc your classes or kill your process at any time. Also the Android concept does not really have the concept of "closing app" actions the same way websites don't have it. So before you continue reading I urge you to rethink your requirements.

That being said. Here are some tips: My understanding of Service#onTaskRemoved(Intent) is that it is only executed if you kill the app through task switcher, so I don't know if this is useful to you. In your instance I would keep a activity ref counter in the application object (+1 for every onResume(), -1 for every onPause() of any activity). With this you can check if your user has active UIs. Usually if you pressed back on the last activity that comes as close to the paradigm "closing" an app. Then just start your task at that point from the application object (this will probably be the last to get gc) or if that doesn't work try an unbound service the most uncoupled component you can generate.

Another, very very bad solution is overriding the finalize() method in an object (e.g. your activity). There are only very, very few reasons to use it since it will trigger an additional gc cycle and your code will be run on the main thread, but it is a way to execute code if the object is about to be gc'ed. Therefore it is discouraged to use by the android team, only use it if you have a gun up your head.

Patrick
  • 33,984
  • 10
  • 106
  • 126