谷歌官方Android应用架构库——处理生命周期

时间:2022-01-24 08:58:37

架构库版本:1.0.0 Alpha 2 - June 2, 2017

android.arch.lifecycle 包提供了可以用于构建生命周期感知(lifecycle-aware)组件的类和接口,这些组件可以根据 Activity 或 Fragment 的当前生命周期自动调整其行为。

:导入 android.arch.lifecycle 到项目中请参看添加组件到项目。

Android Framework 中定义的大多数应用程序组件都附有生命周期。这些生命周期由操作系统或在进程中运行的框架代码管理。它们是 Android 运行的核心并且应用程序必须遵守它们,不这样做可能会导致内存泄漏甚至应用程序崩溃。

假设我们有一个需要在屏幕上显示设备位置的 Activity。常见的实现方式可能如下所示:

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

void start() {
// 链接到系统定位服务
}

void stop() {
// 断开系统定位服务
}
}

class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;

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

public void onStart() {
super.onStart();
myLocationListener.start();
}

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

尽管这个例子看起来不错,在真正的应用程序中,你最终会有太多的类似调用并且会导致 onStart() 和 onStop() 方法变的非常臃肿。

另外,一些组件不能在 onStart() 方法中立即启动。如果我们需要在启动位置观察者之前检查一些配置怎么办?在某些情况下很可能发生 Activity 停止后才检查配置完成,这意味着在 myLocationListener.stop() 被调用之后 myLocationListener.start() 才被调用,会导致定位服务基本上永远保持连接。

class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;

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

public void onStart() {
super.onStart();
Util.checkUserStatus(result -> {
// 如果这个回调在 Activity 停止后被调用怎么办?
if (result) {
myLocationListener.start();
}
});
}

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

android.arch.lifecycle 包提供了类和接口帮助以弹性和隔离的方式解决这些问题。

Lifecycle

Lifecycle 是一个类,它持有关于组件(如 Activity 或 Fragment)生命周期状态的信息,并且允许其他对象观察此状态。

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

  • Event:生命周期事件是从框架和 Lifecycle 发出的事件。这些事件映射到 Activity 和 Fragment 中的回调事件。

  • State:Lifecycle 对象跟踪的组件的当前状态。

谷歌官方Android应用架构库——处理生命周期

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

类可以通过向其方法添加注释来监控组件的生命周期状态。

public class MyObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
}

@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
}
}
aLifecycleOwner.getLifecycle().addObserver(new MyObserver());

LifecycleOwner

LifecycleOwner 是一个单一方法的接口,它表示实现类具有一个 Lifecycle。它有一个 getLifecycle()) 方法,该方法必须由实现类实现。

该实现类从个别的类(例如 Activity 和 Fragment)提取生命周期的所有权,并且允许编写可与两者兼容的组件。任何自定义应用程序类都可以实现 LifecycleOwner 接口。

注:由于 Architecture Components 处于 alpha 阶段,所以 Fragment 和 AppCompatActivity 不能实现 LifecycleOwner (因为我们不能在稳定的组件中添加依赖不稳定的API)。在 Lifecycle 稳定之前,为了方便提供了 LifecycleActivity 和 LifecycleFragment 类。在 Lifecycles 项目发布后,支持库中的 Fragment 和 Activity 将会实现 LifecycleOwner 接口;届时 LifecycleActivity 和 LifecycleFragment 将会被弃用。另请参阅在自定义 Activity 和 Fragment 中实现 LifecycleOwner。

对于上面的例子,我们可以使 MyLocationListener 类成为 LifecycleObserver 然后在 onCreate 中使用 Lifecycle 初始化它。这样让 MyLocationListener 类自给自足,意味着在必要的时候它能对自己进行清理。

class MyActivity extends LifecycleActivity {
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 类允许其他对象查询当前状态。

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

@OnLifecycleEvent(Lifecycle.Event.ON_START)
void start() {
if (enabled) {
// 连接
}
}

public void enable() {
enabled = true;
if (lifecycle.getState().isAtLeast(STARTED)) {
// 如果没有连接则进行连接
}
}

@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop() {
// 如果已经连接则断开连接
}
}

通过这种实现,LocationListener 类是完全的生命周期感知(lifecycle-aware);它可以进行自己的初始化或清理操作,而不受其 Activity 的管理。如果需要在其它的 Activity 或其他的 Fragment 中使用 LocationListener,只需要初始化它。所有的安装和卸载操作都由类自己管理。

可以与 Lifecycle 一起使用的类称为生命周期感知(lifecycle-aware) 组件。鼓励有需要使用 Android 生命周期的类的库提供生命周期感知(lifecycle-aware) 组件,以便于用户可以轻松的在客户端集成这些类,而不需要手动管理生命周期。

LiveData 是一个生命周期感知(lifecycle-aware) 组件的示例。与 ViewModel 一起使用 LiveData 可以在遵守 Android 生命周期的前提下,更容易地使用数据填充UI。

Lifecycles 的最佳实践

  • 保持 UI 控制器(Activity 和 Fragment)尽可能的精简。它们不应该试图去获取它们所需的数据;相反,要用 ViewModel 来获取,并且观察 LiveData 将数据变化反映到视图中。

  • 尝试编写数据驱动(data-driven)的 UI,即 UI 控制器的责任是在数据改变时更新视图或者将用户的操作通知给 ViewModel。

  • 将数据逻辑放到 ViewModel 类中。ViewModel 应该作为 UI 控制器和应用程序其它部分的连接服务。注意:不是由 ViewModel 负责获取数据(例如:从网络获取)。相反,ViewModel 调用相应的组件获取数据,然后将数据获取结果提供给 UI 控制器。

  • 使用 Data Binding 来保持视图和 UI 控制器之间的接口干净。这样可以让视图更具声明性,并且尽可能减少在 Activity 和 Fragment 中编写更新代码。如果你喜欢在 Java 中执行该操作,请使用像 Butter Knife 这样的库来避免使用样板代码并进行更好的抽象化。

  • 如果 UI 很复杂,可以考虑创建一个 Presenter 类来处理 UI 的修改。虽然通常这样做不是必要的,但可能会让 UI 更容易测试。

  • 不要在 ViewModel 中引用 View 或者 Activity 的 context。因为如果 ViewModel 存活的比 Activity 时间长(在配置更改的情况下),Activity 将会被泄漏并且无法被正确的回收。

附录

在自定义 Activity 和 Fragment 中实现 LifecycleOwner

任何自定义的 Fragment 或 Activity 都可以通过实现内置的 LifecycleRegistryOwner 接口转换为 LifecycleOwner(而不是继承 LifecycleActivity 或 LifecycleFragment)

public class MyFragment extends Fragment implements LifecycleRegistryOwner {
LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);

@Override
public LifecycleRegistry getLifecycle() {
return lifecycleRegistry;
}
}

如果要创建一个 LifecycleOwner 的自定义类,可以使用 LifecycleRegistry 类,但是需要将事件转发到该自定义类中。如果是 Fragment 和 Activity 实现了 LifecycleRegistryOwner 接口,则此转发会自动完成。