Search code examples
androidandroid-lifecycle

How does Android go about freeing up memory when low, and how does it restart them?


Please note the following vocabulary to avoid confusion

1) When I say reinstantiating an activity, I mean Activity.onCreate(bundle)

2) When I say starting from scratch an activity, I mean Activity.onCreate(null)

(Although I am not sure it makes a difference) For the purposes of this post, please assume that the Android applications I am interested in all have their activities launched in the same task

How does Android go about freeing memory from apps?

Lets say I have a phone. I am using many applications at once. I got bored of the current Snapchat application. So I press the home button and open Facebook. Note that I did not press the back button, and so Snapchat lives on in the recent apps overview.

Suddenly for some reason the phone is very low on memory and needs to start forcefully freeing up some from background apps.

  • Will the Android OS kill some activities of Snapchat in order to free memory

  • Will the Android OS kill all activities of Snapchat in order to free memory

How does Android go about managing "killed" apps?

Now lets say I want to go back to Snapchat. I hit the overview button and select Snapchat from the scrolling list of recently used apps.

  • Will the Android OS reinstantiate some activities of Snapchat

  • Will the Android OS reinstantiate all activities of Snapchat

  • Will the Android OS reinstantiate zero activities of Snapchat and just starting from scratch with the initial launcher activity.


Solution

  • TL;DRD: all activities are killed.

    The ActivityManagerService doesn't seem to kill anything anymore (it just forces GC calls and dispatches trim events).

    if (false) {
       // For now we won't do this; our memory trimming seems
       // to be good enough at this point that destroying
       // activities causes more harm than good.
       if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE 
                && app != mHomeProcess && app != mPreviousProcess) {
           // Need to do this on its own message because the stack may not
           // be in a consistent state at this point.
           // For these apps we will also finish their activities
           // to help them free memory.
           mStackSupervisor.scheduleDestroyAllActivities(app, "trim");
       }
     }
    

    Though when (if) it did try to kill something it would kill the whole process of a background app (it would simply call kill -9 [PID]). And since by default all your activities are run under one PID they all get destroyed when a process dies.

    The same goes to the OOM and Low Memory Killers - the two operate with processes and make no distinction between your activities.

    Please note, however, that some people claim that Android sometimes kills particular activities one by one on low memory. I failed to find any evidence of that in AOSP.

    ==============
    Now, about restoring your app. I might not be an expert here cause this is connected to the Android's "Tasks and Back Stack System" which is way trickier than their tutorial videos make it seem.

    The operation starts with calling ActivityManager#moveTaskToFront(...) and it eventually leads us to ActivityManagerService#moveTaskToFrontLocked(...)

    There the OS will try to retrieve the stored task record associated with your app. If your app's been destroyed, the manager will have to go to ActivityStackSupervisor#restoreRecentTaskLocked(...) and if all goes well it's gona find the task and the back stack and do this for every activity in the task:

    final ArrayList<ActivityRecord> activities = task.mActivities;
    for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
       final ActivityRecord r = activities.get(activityNdx);
       mWindowManager.addAppToken(0, r.appToken, task.taskId, stack.mStackId,
         r.info.screenOrientation, r.fullscreen,
        (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
           r.userId, r.info.configChanges, task.voiceSession != null,
          r.mLaunchTaskBehind);
    }
    

    The same method (WindowManager#addAppToken(...)) is called when you start a new activity and it goes with a comment:

    // Here it is!  Now, if this is not yet visible to the
    // user, then just add it without starting; it will
    // get started when the user navigates back to it.
    

    So my guess is that when the OS restores an app it recreates the activity stack fully but only the topmost activity actually actually gets restarted. I guess, it's easy to set up an experiment to find out if I'm right here.

    =======
    One more update :) Yep, these two methods (ActivityStackSupervisor#resumeTopActivitiesLocked() and ActivityStackSupervisor#resumeTopActivityLocked(...)) are called a bit later. Though I really don't undesratnd why they resume activities in more than one stack - as I said, I'm definitely not an expert here.