使用生命周期感知组件处理生命周期   Android Jetpack 的一部分。

生命周期感知组件会响应另一个组件(如活动和片段)的生命周期状态变化而执行操作。这些组件可帮助您生成组织更完善、更轻便且更易于维护的代码。

常见模式是在活动和片段的生命周期方法中实现依赖组件的操作。但是,这种模式会导致代码组织不善以及错误增多。通过使用生命周期感知组件,您可以将依赖组件的代码从生命周期方法中移出,并将其移入组件本身。

androidx.lifecycle 包提供了类和接口,可让您构建 *生命周期感知* 组件,这些组件可以根据活动或片段的当前生命周期状态自动调整其行为。

Android 框架中定义的大多数应用组件都附带生命周期。生命周期由您的进程中运行的操作系统或框架代码管理。它们是 Android 工作方式的核心,您的应用必须尊重它们。不这样做可能会触发内存泄漏,甚至会导致应用崩溃。

假设我们有一个在屏幕上显示设备位置的活动。常见的实现可能如下所示

Kotlin

internal class MyLocationListener(
        private val context: Context,
        private val callback: (Location) -> Unit
) {

    fun start() {
        // connect to system location service
    }

    fun stop() {
        // disconnect from system location service
    }
}

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
    }

    public override fun onStart() {
        super.onStart()
        myLocationListener.start()
        // manage other components that need to respond
        // to the activity lifecycle
    }

    public override fun onStop() {
        super.onStop()
        myLocationListener.stop()
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

Java

class MyLocationListener {
    public MyLocationListener(Context context, Callback callback) {
        // ...
    }

    void start() {
        // connect to system location service
    }

    void stop() {
        // disconnect from system location service
    }
}

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    @Override
    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, (location) -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        myLocationListener.start();
        // manage other components that need to respond
        // to the activity lifecycle
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

尽管此示例看起来不错,但在实际应用中,最终会进行太多调用来管理响应生命周期当前状态的 UI 和其他组件。管理多个组件会将大量代码置于生命周期方法中,例如 onStart()onStop(),这使得它们难以维护。

此外,无法保证组件在活动或片段停止之前启动。 如果需要执行长时间运行的操作,例如在onStart()中进行一些配置检查,则尤其如此。 这会导致竞争条件,其中onStop()方法在onStart()之前完成,从而使组件保持活动状态的时间超过了所需的时间。

Kotlin

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
    }

    public override fun onStart() {
        super.onStart()
        Util.checkUserStatus { result ->
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start()
            }
        }
    }

    public override fun onStop() {
        super.onStop()
        myLocationListener.stop()
    }

}

Java

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, location -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start();
            }
        });
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}

androidx.lifecycle 包提供了一些类和接口,可帮助您以弹性和隔离的方式解决这些问题。

生命周期

Lifecycle 是一个类,它保存有关组件(如活动或片段)的生命周期状态的信息,并允许其他对象观察此状态。

Lifecycle 使用两个主要的枚举来跟踪其关联组件的生命周期状态

事件
从框架和 Lifecycle 类分派的 lifecycle 事件。 这些事件映射到活动和片段中的回调事件。
状态
Lifecycle 对象跟踪的组件的当前状态。
Diagram of lifecycle states
图 1. 构成 Android 活动生命周期的状态和事件

将状态视为图的节点,将事件视为这些节点之间的边。

一个类可以通过实现DefaultLifecycleObserver 并覆盖相应的方法(如 onCreateonStart 等)来监控组件的生命周期状态。 然后,您可以通过调用 addObserver() 方法(Lifecycle 类的实例)并传递观察者的实例来添加观察者,如以下示例所示

Kotlin

class MyObserver : DefaultLifecycleObserver {
    override fun onResume(owner: LifecycleOwner) {
        connect()
    }

    override fun onPause(owner: LifecycleOwner) {
        disconnect()
    }
}

myLifecycleOwner.getLifecycle().addObserver(MyObserver())

Java

public class MyObserver implements DefaultLifecycleObserver {
    @Override
    public void onResume(LifecycleOwner owner) {
        connect()
    }

    @Override
    public void onPause(LifecycleOwner owner) {
        disconnect()
    }
}

myLifecycleOwner.getLifecycle().addObserver(new MyObserver());

在上面的示例中,myLifecycleOwner 对象实现了 LifecycleOwner 接口,该接口将在下一节中介绍。

LifecycleOwner

LifecycleOwner 是一个单方法接口,表示该类具有一个 Lifecycle。 它有一个方法,getLifecycle(),该方法必须由该类实现。 如果您尝试管理整个应用程序进程的生命周期,请参阅 ProcessLifecycleOwner

此接口将 Lifecycle 的所有权从各个类(如 FragmentAppCompatActivity)中抽象出来,并允许编写使用它们的组件。 任何自定义应用程序类都可以实现 LifecycleOwner 接口。

实现 DefaultLifecycleObserver 的组件与实现 LifecycleOwner 的组件无缝协作,因为所有者可以提供一个生命周期,观察者可以注册以进行监视。

对于位置跟踪示例,我们可以让 MyLocationListener 类实现 DefaultLifecycleObserver,然后在 onCreate() 方法中使用活动的 Lifecycle 初始化它。 这允许 MyLocationListener 类自成一体,这意味着对生命周期状态变化做出反应的逻辑是在 MyLocationListener 中声明的,而不是在活动中。 让各个组件存储自己的逻辑使得活动和片段的逻辑更易于管理。

Kotlin

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this, lifecycle) { location ->
            // update UI
        }
        Util.checkUserStatus { result ->
            if (result) {
                myLocationListener.enable()
            }
        }
    }
}

Java

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
            // update UI
        });
        Util.checkUserStatus(result -> {
            if (result) {
                myLocationListener.enable();
            }
        });
  }
}

一个常见的用例是,如果 Lifecycle 目前不在良好状态,则避免调用某些回调。 例如,如果回调在保存活动状态后运行片段事务,它将触发崩溃,因此我们永远不想调用该回调。

为了使此用例变得容易,Lifecycle 类允许其他对象查询当前状态。

Kotlin

internal class MyLocationListener(
        private val context: Context,
        private val lifecycle: Lifecycle,
        private val callback: (Location) -> Unit
): DefaultLifecycleObserver {

    private var enabled = false

    override fun onStart(owner: LifecycleOwner) {
        if (enabled) {
            // connect
        }
    }

    fun enable() {
        enabled = true
        if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            // connect if not connected
        }
    }

    override fun onStop(owner: LifecycleOwner) {
        // disconnect if connected
    }
}

Java

class MyLocationListener implements DefaultLifecycleObserver {
    private boolean enabled = false;
    public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
       ...
    }

    @Override
    public void onStart(LifecycleOwner owner) {
        if (enabled) {
           // connect
        }
    }

    public void enable() {
        enabled = true;
        if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

    @Override
    public void onStop(LifecycleOwner owner) {
        // disconnect if connected
    }
}

使用此实现,我们的 LocationListener 类完全是生命周期感知的。 如果我们需要从另一个活动或片段中使用我们的 LocationListener,我们只需要初始化它。 所有设置和拆卸操作都由类本身管理。

如果库提供需要与 Android 生命周期协作的类,建议您使用生命周期感知组件。 您的库客户端可以轻松地集成这些组件,而无需在客户端端进行手动生命周期管理。

实现自定义 LifecycleOwner

支持库 26.1.0 及更高版本中的片段和活动已经实现了 LifecycleOwner 接口。

如果您有一个自定义类,您想将其作为 LifecycleOwner,您可以使用 LifecycleRegistry 类,但您需要将事件转发到该类中,如以下代码示例所示

Kotlin

class MyActivity : Activity(), LifecycleOwner {

    private lateinit var lifecycleRegistry: LifecycleRegistry

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        lifecycleRegistry = LifecycleRegistry(this)
        lifecycleRegistry.markState(Lifecycle.State.CREATED)
    }

    public override fun onStart() {
        super.onStart()
        lifecycleRegistry.markState(Lifecycle.State.STARTED)
    }

    override fun getLifecycle(): Lifecycle {
        return lifecycleRegistry
    }
}

Java

public class MyActivity extends Activity implements LifecycleOwner {
    private LifecycleRegistry lifecycleRegistry;

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

        lifecycleRegistry = new LifecycleRegistry(this);
        lifecycleRegistry.markState(Lifecycle.State.CREATED);
    }

    @Override
    public void onStart() {
        super.onStart();
        lifecycleRegistry.markState(Lifecycle.State.STARTED);
    }

    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return lifecycleRegistry;
    }
}

生命周期感知组件的最佳实践

  • 使您的 UI 控制器(活动和片段)尽可能精简。 他们不应该尝试获取自己的数据; 相反,使用 ViewModel 来执行此操作,并观察 LiveData 对象以将更改反映回视图。
  • 尝试编写数据驱动的 UI,其中您的 UI 控制器负责更新数据更改的视图,或将用户操作通知回 ViewModel
  • 将您的数据逻辑放在您的 ViewModel 类中。 ViewModel 应作为 UI 控制器和应用程序其余部分之间的连接器。 但请注意,它不是 ViewModel 的责任来获取数据(例如,从网络)。 相反,ViewModel 应该调用适当的组件来获取数据,然后将结果提供回 UI 控制器。
  • 使用 数据绑定 来维护视图和 UI 控制器之间干净的接口。 这使您可以使您的视图更具声明性,并最大程度地减少您需要在活动和片段中编写的更新代码。 如果您希望在 Java 编程语言中执行此操作,请使用像 Butter Knife 这样的库来避免样板代码并具有更好的抽象。
  • 如果您的 UI 很复杂,请考虑创建一个 presenter 类来处理 UI 修改。 这可能是一项费力的任务,但它可以使您的 UI 组件更易于测试。
  • 避免在您的 ViewModel 中引用 ViewActivity 上下文。 如果 ViewModel 超过了活动的生命周期(在配置更改的情况下),您的活动会泄漏,并且不会被垃圾回收器正确释放。
  • 使用 Kotlin 协程 来管理长时间运行的任务和其他可以异步运行的操作。

生命周期感知组件的用例

生命周期感知组件可以使您更轻松地在各种情况下管理生命周期。 一些示例是

  • 在粗粒度和细粒度位置更新之间切换。 使用生命周期感知组件在您的位置应用程序可见时启用细粒度位置更新,并在应用程序处于后台时切换到粗粒度更新。 LiveData(一个生命周期感知组件)允许您的应用程序在用户更改位置时自动更新 UI。
  • 停止和启动视频缓冲。 使用生命周期感知组件尽快启动视频缓冲,但延迟播放,直到应用程序完全启动。 您还可以使用生命周期感知组件在应用程序被销毁时终止缓冲。
  • 启动和停止网络连接。 使用生命周期感知组件在应用程序处于前台时启用网络数据的实时更新(流式传输),并在应用程序进入后台时自动暂停。
  • 暂停和恢复动画可绘制对象。 使用生命周期感知组件来处理在应用程序处于后台时暂停动画可绘制对象,并在应用程序处于前台后恢复可绘制对象。

处理停止事件

Lifecycle 属于 AppCompatActivityFragment 时,Lifecycle 的状态将更改为 CREATED,并且当 AppCompatActivityFragmentonSaveInstanceState() 被调用时,将分派 ON_STOP 事件。

FragmentAppCompatActivity 的状态通过 onSaveInstanceState() 保存时,它的 UI 将被视为不可变,直到调用 ON_START。 尝试在保存状态后修改 UI 可能会导致应用程序导航状态不一致,这就是为什么 FragmentManager 在保存状态后运行 FragmentTransaction 时会引发异常。 有关详细信息,请参阅 commit()

LiveData 通过在观察者的关联 Lifecycle 至少处于 STARTED 状态之前才调用观察者,从而避免了这种边缘情况。在幕后,它在决定是否调用观察者之前会调用 isAtLeast()

不幸的是,AppCompatActivityonStop() 方法是在 onSaveInstanceState() 之后调用的,这会导致一个间隙,在此间隙中不允许进行 UI 状态更改,但 Lifecycle 尚未移动到 CREATED 状态。

为了防止这个问题,beta2 及更低版本中的 Lifecycle 类将状态标记为 CREATED,但不调度事件,以便任何检查当前状态的代码都能获得真实值,即使事件直到系统调用 onStop() 才会调度。

不幸的是,此解决方案存在两个主要问题

  • 在 API 级别 23 及更低版本中,Android 系统实际上会保存活动的 state,即使它被另一个活动部分覆盖。换句话说,Android 系统会调用 onSaveInstanceState(),但它不一定调用 onStop()。这会创建一个潜在的较长间隔,在此间隔中,观察者仍然认为生命周期处于活动状态,即使它的 UI 状态无法修改。
  • 任何想要公开与 LiveData 类类似行为的类都必须实现由 beta 2 及更低版本中的 Lifecycle 提供的解决方法。

其他资源

要详细了解如何使用生命周期感知组件处理生命周期,请参阅以下其他资源。

示例

Codelabs

博客