9

The Android OS kills processes when it's low on memory. Scenario: Android kills the app process and I re-open it through either the Android launcher or the recent-task list (long press home button). I can check if Android killed my app process in the onCreate() method of the most recently viewed activity using:

@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);

    if (savedInstanceState != null) {
        // Re-initialise things that killing the app would have destroyed
    }
}

However, if Android kills the app process and I re-open it through a Notification using an Intent packaged inside a PendingIntent, I don't know how to determine if the app process was killed by Android. Subsequently I do not re-initialise things that killing the app process would have destroyed.

Is there a way to determine if Android killed the application process when opening a new Activity from a Notification?

I have found one hacky solution to this problem. Using: Android: always launch top activity when clicked on notification I can open the activity on top of the stack which is passed a savedInstanceState if Android killed the app process and deal with re-initialisation. Each activity is then responsible for redirecting the user to the appropriate activity using Extras in the original Notification Intent. Intent setup for this scenario is below:

Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);

Is there an Action, Category or Flag I can set on an Intent that will emulate re-opening the app process as if done by the user but on a new Intent / Activity?

EDIT: To clarify the last question (although it seems my infant understanding of Android is failing me so it probably doesn't make sense): Is there an Action, Category or Flag I can set on an Intent, like in the snippet above, that will allow me to determine if the app process has been killed by the OS?

Community
  • 1
  • 1
willjgriff
  • 783
  • 2
  • 6
  • 11
  • "I can check if Android killed my app in the onCreate() method of the most recently viewed activity using" -- not really. That tells you if Android is returning to an existing task, and one that was fairly recent, in which case you are getting saved instance state associated with your prior activity instance in that task. After a while, Android will tend to reset the task and start clean (though there's an attribute you can put in the manifest to disable this). The user can also get rid of your task, and on Android 4.4 and below a reboot wipes out tasks. – CommonsWare Apr 17 '15 at 14:24
  • Plus, on Android 4.4, the task list seems capped (whereas on Android 5.0, the task list seems to grow indefinitely). Do not rely upon the existence of a saved instance state `Bundle` to tell you whether or not your process had been terminated. "Subsequently I do not re-initialise things that killing the app would have destroyed" -- again, this will not only be due to a `Notification`. It will also occur for any other non-task-related startup of your process (e.g., `AlarmManager` triggering a `BroadcastReceiver`). Your entry points need to be able to handle lazy-initializing as needed. – CommonsWare Apr 17 '15 at 14:24
  • "I can open the activity on top of the stack which is passed a savedInstanceState if Android killed the app and deal with re-initialisation" -- only if the task is still there and has not been reset. Finally, you may wish to clarify what you mean by "emulate re-opening the app as if done by the user but on a new Intent / Activity". – CommonsWare Apr 17 '15 at 14:25
  • So if the task is destroyed can I open the app from the running apps list menu? And if so to what activity would I be directed? (I assume the original launch activity to start the app fresh, in which case this isn't a problem). The entry points are generally able to handle lazy-initialization apart from this case where if Android killed the app I need to initialise in a different way. Is there no reliable way to tell if Android has killed the application? Whether or not it's the right way to do it? (Because the right way I expect would require large restructuring and I'm still learning) – willjgriff Apr 17 '15 at 15:01
  • "So if the task is destroyed can I open the app from the running apps list menu?" -- Android does not have a "running apps list". If you are referring to the recent-tasks list (a.k.a., overview screen), then the task would not be there if the user got rid of it (e.g., by swiping). If you are referring to some manufacturer-specific "running apps list menu", I cannot comment on that. "Is there no reliable way to tell if Android has killed the application?" -- the only reason you would care is if you have app-level caches (e.g., singletons). If they're `null`, you are running in a new process. – CommonsWare Apr 17 '15 at 15:10
  • I appreciate the help and I apologise for the incorrect terminology. I was indeed referring to the recent-tasks list. The app in question does use singletons so for now I'll try to use that to determine the state of the app. Thanks for your help. – willjgriff Apr 17 '15 at 16:23
  • "I was indeed referring to the recent-tasks list" -- the key is that it is a list of recent tasks. A given task may or may not have a process (or processes) associated with it. For example, on Android 5.0+, the recent-tasks list is now called the overview screen, because the tasks are pretty much everything that you have run since you bought the phone, and so some of those tasks are not that recent. :-) – CommonsWare Apr 17 '15 at 16:35

2 Answers2

17

The easiest way to determine if Android has killed the process and then created a new process is as follows:

In your root Activity (the one with ACTION=MAIN and CATEGORY=DEFAULT) create a public static boolean variable like this:

public static boolean initialized;

in onCreate() of your root Activity, set this variable to true.

In onCreate() of all your other activities, you can check if Android has killed/recreated the task by checking the state of the boolean, and if the app hasn't been initialized, you can redirect to the root Activity or call an initialization method or whatever... like this:

if (!RootActivity.initialized) {
    // Android has killed and recreated the process and launched this
    //  Activity. We need to reinitialize everything now
    ... redirect to root activity or call reinitialize method
}
David Wasser
  • 93,459
  • 16
  • 209
  • 274
  • Thanks David. I ended up doing something similar but not quite the same. This app can re-enter on an activity that isn't the root (I still don't know what the expected fundamental structure of the activities are in all honesty which is probably why this app can do this. It's also a little too deep into development to alter now). In the master Activity that all others inherit from, in onResume() I set a static 'activityLaunched' boolean that lives in the Application object to true. It's default is false. – willjgriff Apr 30 '15 at 11:08
  • Twinned with another boolean that lives in the SharedPreferences that is set when the user manually leaves the app (I have to catch all instances of the user leaving manually otherwise it will indicate Android ended the process, not great, I know) I can determine whether the user or the OS ended the process. Your solution is better. I will give it whirl at some point. Thanks! – willjgriff Apr 30 '15 at 11:13
  • But, Activity will be recreated when rotate the device. You need to have this field in Application scope. – tim4dev Jan 09 '19 at 16:25
  • @tim4dev a `static` variable's lifetime is the lifetime of the OS process hosting the application. It doesn't go away when the `Activity` is recreated. – David Wasser Jan 09 '19 at 21:14
10

Since the process id will change when app is being killed and restarted, you can use this to check it:

In onSaveInstanceState(Bundle outState) get current process id and save it in outState:

onSavedInstanceState(Bundle outState){
   super.onSaveInstanceState(outState);
   outState.putInt("my_pid", Process.myPid());
}

Then in onCreate(Bunde savedInstanceState) compare the saved process id and the current process id:

onCreate(Bundle savedInstanceState){
   if(savedInstanceState!=null){            
      if(savedInstanceState.getInt("my_pid",-1)==android.os.Process.myPid())
         // app was not killed    
      else
        // app was killed
    }
}
Thomas Fritsch
  • 9,639
  • 33
  • 37
  • 49
colens
  • 467
  • 3
  • 12
  • I combined the answers of @DavidWasser and @colens : `static int pid=-1` , initialized with: `pid = android.os.Process.myPid()` and checked it with : `if(pid != android.os.Process.myPid()` (this is after trying only David's answer and failing to do this in run time - the boolean was true, although all my memory was dumped – Yoav R. Jul 03 '18 at 22:19
  • 1
    Process id (android.os.Process.myPid()) won't change on restart (at least on Android 9 and 10) – localhost Aug 22 '19 at 19:35
  • That is not guaranteed for onSaveInstanceState() to be called when android system kills the app – ulmaxy May 20 '20 at 09:05