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

生命周期感知型组件会根据另一个组件(例如 activity 和 fragment)的生命周期状态变化来执行操作。这些组件有助于您生成井然有序、通常更轻量、更易于维护的代码。

一种常见的模式是在 activity 和 fragment 的生命周期方法中实现依赖组件的操作。然而,这种模式会导致代码组织不良并容易产生错误。通过使用生命周期感知型组件,您可以将依赖组件的代码从生命周期方法中移出,并放到组件本身中。

androidx.lifecycle 软件包提供了类和接口,让您能够构建 lifecycle-aware 组件—这些组件可以根据 activity 或 fragment 的当前生命周期状态自动调整其行为。

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

假设我们有一个 activity,它在屏幕上显示设备位置。一种常见的实现方式可能如下所示

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(),这使得维护变得困难。

此外,无法保证组件会在 activity 或 fragment 停止之前启动。如果我们需要在 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 是一个类,它保存着组件(如 activity 或 fragment)生命周期状态的信息,并允许其他对象观察此状态。

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

Event
从框架和 Lifecycle 类分派的生命周期事件。这些事件映射到 activity 和 fragment 中的回调事件。
State
Lifecycle 对象跟踪的组件当前状态。
图 1. Android activity 生命周期的状态和事件。

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

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

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,然后在 activity 的 onCreate() 方法中用 activity 的 Lifecycle 初始化它。这使得 MyLocationListener 类可以自给自足,这意味着响应生命周期状态变化的逻辑声明在 MyLocationListener 中而不是 activity 中。让单个组件存储自己的逻辑使得 activity 和 fragment 的逻辑更易于管理。

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 当前状态不佳,则避免调用某些回调。例如,如果在保存 activity 状态后回调运行 fragment 事务,则会触发崩溃,因此我们绝不会调用该回调。

为了简化此用例,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 类完全是生命周期感知型的。如果我们想在另一个 activity 或 fragment 中使用 LocationListener,只需对其进行初始化即可。所有设置和清理操作都由类本身管理。

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

实现自定义 LifecycleOwner

Support Library 26.1.0 及更高版本中的 Fragment 和 Activity 已实现 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 控制器(activity 和 fragment)精简。它们不应尝试自行获取数据;而是使用 ViewModel 来执行此操作,并观察 LiveData 对象以将更改反映回视图。
  • 尝试编写数据驱动的 UI,其中 UI 控制器的职责是随着数据变化更新视图,或将用户操作通知回 ViewModel
  • 将您的数据逻辑放在 ViewModel 类中。ViewModel 应该充当您的 UI 控制器与应用其余部分之间的连接器。但请注意,获取数据(例如从网络获取)并不是 ViewModel 的职责。相反,ViewModel 应该调用相应的组件来获取数据,然后将结果提供回 UI 控制器。
  • 使用 Data Binding 在您的视图和 UI 控制器之间维护一个清晰的接口。这允许您让视图更具声明性,并最大限度地减少需要在 activity 和 fragment 中编写的更新代码。如果您更喜欢使用 Java 编程语言进行此操作,请使用 Butter Knife 等库来避免样板代码并拥有更好的抽象。
  • 如果您的 UI 很复杂,请考虑创建 presenter 类来处理 UI 修改。这可能是一项艰巨的任务,但可以使您的 UI 组件更容易测试。
  • 避免在您的 ViewModel 中引用 ViewActivity 上下文。如果 ViewModel 的生命周期长于 activity(在配置更改的情况下),您的 activity 将会泄漏,并且不会被垃圾回收器正确处理。
  • 使用 Kotlin 协程来管理长时间运行的任务以及其他可以异步运行的操作。

生命周期感知型组件的用例

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

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

处理 on stop 事件

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

FragmentAppCompatActivity 的状态通过 onSaveInstanceState() 保存后,其 UI 被认为是不可变的,直到调用 ON_START。在保存状态后尝试修改 UI 可能会导致应用导航状态不一致,这就是为什么如果在保存状态后应用运行 FragmentTransactionFragmentManager 会抛出异常的原因。详情请参阅 commit()

LiveData 通过避免在其观察者的关联 Lifecycle 状态至少不是 STARTED 时调用其观察者,从而默认防止了此边缘情况。在幕后,它在决定调用观察者之前会调用 isAtLeast()

不幸的是,AppCompatActivityonStop() 方法在 onSaveInstanceState() 之后被调用,这留下了一个间隙,在该间隙中不允许更改 UI 状态,但 Lifecycle 尚未移动到 CREATED 状态。

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

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

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

其他资源

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

示例

  • Sunflower,一个演示架构组件最佳实践的示例应用

Codelab

博文