使用生命周期感知组件处理生命周期   作为 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 对象跟踪的组件的当前状态。
Diagram of lifecycle states
图 1. 构成 Android 活动生命周期的状态和事件

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

一个类可以通过实现 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,然后在 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

Support Library 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 可能会导致应用程序导航状态不一致,这就是为什么如果应用程序在保存状态后运行 FragmentTransactionFragmentManager 会抛出异常。有关详细信息,请参阅 commit()

LiveData 通过避免在观察者的关联 Lifecycle 至少处于 STARTED 状态时调用其观察者来防止这种情况。在后台,它会在决定调用其观察者之前调用 isAtLeast()

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

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

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

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

其他资源

要了解有关使用生命周期感知组件处理生命周期的更多信息,请参阅以下其他资源。

示例

Codelabs

博客