ProcessLifecycleOwner
With a name like ProcessLifecycleOwner, you might think that this modeled the lifecycle of a process. Then, you quickly realize that this makes little sense, as the only “lifecycle” that a process goes through is creation and termination, and we cannot get control in the latter event.
Instead, ProcessLifecycleOwner might better be named ForegroundLifecycleOwner. ProcessLifecycleOwner models the lifecycle of all activities combined:
-
ON_CREATEis triggered when the process starts up -
ON_STARTandON_RESUMEare triggered when an activity goes through those lifecycle events, and no other activity had been started recently -
ON_PAUSEandON_STOPare triggered, after a delay, when an activity goes through those lifecycle events, if another activity is not started and resumed by this time -
ON_DESTROYis never triggered
The delay period is 700ms (as of 1.1.1), so as long as another activity is started and resumed after a prior activity was paused and stopped within 700ms, the process has not undergone a lifecycle change, even though those individual activities did.
So, imagine a single-activity app:
-
ON_CREATEhappens right away -
ON_STARTandON_RESUMEhappen shortly thereafter, assuming that the process is starting because an activity is being displayed - The user rotates the screen, causing the activity to be destroyed and recreated
-
ON_PAUSEandON_STOPdo not occur, because a new activity was started and resumed before theProcessLifecycleOwnerdelay period elapsed -
ON_STARTandON_RESUMEdo not occur, because we did not move through the paused and stopped lifecycle states, even though the new activity instance did - The user presses HOME, BACK, or otherwise leaves this activity for another task
-
ON_PAUSEandON_STOPhappen after the delay period, since no activity from this process went throughON_STARTandON_RESUMEduring that time
Note that this comes at a cost: the extensions artifact automatically adds a <provider> element to your manifest, one that initializes the ProcessLifecycleOwner… even if your app does not use ProcessLifecycleOwner. This is simply so ProcessLifecycleOwner code can be invoked as soon as your process is started.
The General/ProcessLifecycle sample project has a LifecycleApplication that registers itself as an observer of the singleton instance of ProcessLifecycleOwner and dumps all the events to Logcat:
package com.commonsware.android.recyclerview.videolist;
import android.app.Application;
import android.arch.lifecycle.DefaultLifecycleObserver;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.OnLifecycleEvent;
import android.arch.lifecycle.ProcessLifecycleOwner;
import android.support.annotation.NonNull;
import android.util.Log;
public class LifecycleApplication extends Application
implements DefaultLifecycleObserver {
@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
@Override
public void onCreate(@NonNull LifecycleOwner owner) {
Log.d(getClass().getSimpleName(), "ON_CREATE");
}
@Override
public void onStart(@NonNull LifecycleOwner owner) {
Log.d(getClass().getSimpleName(), "ON_START");
}
@Override
public void onResume(@NonNull LifecycleOwner owner) {
Log.d(getClass().getSimpleName(), "ON_RESUME");
}
@Override
public void onPause(@NonNull LifecycleOwner owner) {
Log.d(getClass().getSimpleName(), "ON_PAUSE");
}
@Override
public void onStop(@NonNull LifecycleOwner owner) {
Log.d(getClass().getSimpleName(), "ON_STOP");
}
@Override
public void onDestroy(@NonNull LifecycleOwner owner) {
Log.d(getClass().getSimpleName(), "ON_DESTROY");
}
}
That LifecycleApplication is then registered in the manifest via android:name on <application>:
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.commonsware.android.recyclerview.videolist"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0">
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="false" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="false"
android:name=".LifecycleApplication"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.Apptheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".VideoPlayerActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:launchMode="singleTask"
android:supportsPictureInPicture="true"
android:theme="@style/Theme.Apptheme.NoActionBar" />
<receiver android:name=".RemoteActionReceiver" />
</application>
</manifest>
The app itself is a clone of one from The Busy Coder’s Guide to Android Development. It consists of two activities. One shows a list of all videos indexed by the MediaStore. The other plays back a selected video using a VideoView. And, on Android 8.0+ devices, the video player activity will have a FAB that switches that activity into picture-in-picture mode.
(NOTE: to run this sample, your test device will need 1+ videos)
If you run it, you will see the ON_CREATE, ON_START, and ON_RESUME events logged in rapid succession. And, if you do not press that enticing FAB, and just use the video player in normal mode, ON_PAUSE and ON_STOP get invoked at normal times, such as when the user navigates to some other task (e.g., presses HOME).
The FAB, though, changes things, as it moves the video player to a floating picture-in-picture (PiP) window.
If you tap the FAB, and do not touch anything else for a bit, you will see ON_PAUSE, then ON_RESUME, get logged. This is because:
- The PiP window never has the foreground from an input standpoint, and so its activity is paused, but not stopped (as it is still visible)
- The underlying activity is started and resumed, though with a few seconds’ delay, for inexplicable reasons
Similarly, if you tap the PiP window, to bring up the controls, you will see ON_PAUSE logged, as the list-of-videos activity is paused (it no longer has the foreground input) but the PiP window is not resumed (the input is handled by the system UI, not the activity). After a few moments of inactivity, that PiP window will return to its regular state, and ON_RESUME will be logged.
Playing around with the PiP further (e.g., closing it via the X in the corner) allows you to see how PiP mode ties into activity lifecycles.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.