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:

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:

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:

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.