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_CREATE
is triggered when the process starts up -
ON_START
andON_RESUME
are triggered when an activity goes through those lifecycle events, and no other activity had been started recently -
ON_PAUSE
andON_STOP
are triggered, after a delay, when an activity goes through those lifecycle events, if another activity is not started and resumed by this time -
ON_DESTROY
is 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_CREATE
happens right away -
ON_START
andON_RESUME
happen 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_PAUSE
andON_STOP
do not occur, because a new activity was started and resumed before theProcessLifecycleOwner
delay period elapsed -
ON_START
andON_RESUME
do 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_PAUSE
andON_STOP
happen after the delay period, since no activity from this process went throughON_START
andON_RESUME
during 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.