Android翻译-用带有生命周期感知的组件处理生命周期问题
原文章地址:
https://developer.android.com/topic/libraries/architecture/lifecycle
生命周期感知组件可以随着其他带有生命周期的组件例如activities,fragment等生命状态发生改变时,也相应的执行动作。这些组件帮助我们创造更好组织,更轻量的代码。这样更易于维护。
通用的模式是在activities和fragments的生命周期方法中实现依赖组件的动作,但这种模式会导致不好组织的代码和错误的繁殖。通过使用生命周期感知的组件,你可以将依赖组件的代码从生命周期的方法中移出来,然后移到依赖组件的自己的代码里头。
android.arch.lifecycle 包提供类和接口让你创建具有生命周期感知的组件。这种组件能够基于activity ,fragment的当前生命状态自动调整他们的行为。
定义在android framework 的应用组件是有生命周期的。生命周期是被运行在你的进程中的系统或者framework代码操作。他们是android运行的核心,你的应用程序必须遵守他们。不这样做可能会导致内存泄漏甚至应用崩溃。
想象一下我们现在有个activity是在屏幕上显示设备地址。一个通用的实现可能是像下面这样:
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
}
}
尽管这个例子看起来不错,在真正的app开发中国呢,你最终在面对生命周期状态会有很多调用来管理ui和其他组件。管理多倍的组件放置-个相当大的代码在生命周期方法里,例如onStart()onStop(),这样会很难维护。
而且,并不能保证组件会在会在activity或者fragment stop之前start。尤其是我们执行一个很长时间的操作。例如一些在onstart方法配置的check.这个会造成一种罕见的情况:onStop方法结束在onStart方法之前,使得组件活得比它需要的时间更长。
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();
}
}
android.arch.lifecycle包提供类和接口帮助你解决这些问题用一种适应能力强和独立的方式。
Lifecycle
Lifecycle一个持有组件(例如activity 或者fragment)的生命周期状态的类,并且允许其他对象观察该状态。
Lifecycle使用两种主要的枚举来追踪和他关联的生命周期状态。
event
生命周期事件是被framework 和 Lifecycle类分发的。这些事件映射在activities和fragments的回调方法中。
State
被Lifecycle类追踪的组件当前状态
将这些状态看作涂上的节点并且把这些事件作为这些节点间的边界。
一个类能够监视一个组件的生命周期状态通过添加注解在该类的方法中。然后你能增加一个观察者通过调用Lifecycle类的addObserver()方法,并且传递一个观察者的实例,如下面所示:
public class MyObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void connectListener() {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void disconnectListener() {
...
}
}
myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
在上面的例子中,myLifecycleOwner对象实现了LifecycleOwner接口,在下节会谈到。
LifecycleOwner
LifecycleOwner是一个只有一个方法的接口,该接口表示这个类有一个Lifecycle对象。该接口只有一个方法getLifecycle(),必须被其实现类实现。若是你想管理整个应用程序进程的生命周期,可以查看ProcessLifecycleOwner类。
该接口抽象了单个类对一个Lifecycle对象的所有权,例如Fragment和AppCompatActivity,并且允许重写和他们一起工作的组件。一个定制的application可以实现LifecycleOwner接口。
实现了LifecycleObserver 的组件和实现了LifecycleOwner组件可以无缝工作,因为一个owner能够提供一个lifecycle对象,一个观察者可以注册来监视该lifecycle对象。
对于地址跟踪例子,我们能够写一个实现了LifecycleObserver 接口的MyLocationListener类,并且在activity的onCreate()方法中用activity的lifecycle对象初始化MyLocationListener类。这样能够使MyLocationListener类“自我满足“,也就是对针对生命周期状态改变的作出反应的逻辑在MyLocationListener类中使公开的,而不是在activity中。有了单独组件存储他们自己的逻辑使得activities和fragments的逻辑更容易管理。
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状态是被保存后,callback运行了fragment transaction ,此时将会触发一个crash,所以我们将永远不想调用该callback。
为了使该使用案例容易,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) {
// connect
}
}
public void enable() {
enabled = true;
if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
// connect if not connected
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop() {
// disconnect if connected
}
}
完成实现后,我们的LocationListener类就完全生命周期可感知了,若我们需要在其他activity或者fragment中使用LocationListener,只需要初始化它就可以了,所有的设置和卸载操作都是由LocationListener类自己操作的。
若一个lib库提供需要和android 生命周期一起工作的类,我们建议你需要用生命周期感知的组件,这样你的lib库的客户端能够更容易整合这些组件不用在lib库的客户端手动的生命周期管理。
Implementing a custom LifecycleOwner
在26.1.0版本的组件库的Fragments and Activities已经实现了LifecycleOwner接口。
如果你有定制类想要成为一个LifecycleOwner,你可以使用 LifecycleRegistry 类,但是你需要forward事件到这个类里,正如下面的代码案例显示:
public class MyActivity extends Activity implements LifecycleOwner {
private LifecycleRegistry mLifecycleRegistry;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLifecycleRegistry = new LifecycleRegistry(this);
mLifecycleRegistry.markState(Lifecycle.State.CREATED);
}
@Override
public void onStart() {
super.onStart();
mLifecycleRegistry.markState(Lifecycle.State.STARTED);
}
@NonNull
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
}
Best practices for lifecycle-aware components
- 保持你的UI controllers(activities and
fragments)尽可能的瘦。他们不应该尝试获取数据,相反,使用ViewModel去做这些,然后观察ViewModel对象来返回改变到views。 - 尝试去写数据驱动的uis当你的UI controllers的责任是当数据改变时了更新views,或者通知用户的动作给ViewModel.
- 把你的数据逻辑放到ViewModel类中。ViewModel应当为你的UI controller和其他app联系服务。要主要尽管如此,获取数据(例如从网络)并不是ViewModel的责任,ViewModel应该调用合适的组件来获取数据,然后提供结果返回给UI controller。
- 使用Data binding 来保持views和你的UI controller之间的接口干净。这样允许你使你的views更有说明并且最小化的你需要写在你的activities and fragments中的代码。如果你更喜欢在java 语言中这么做,使用lib库例如Butter Knifet来避免模版化的代码并且有更好的抽象。
- 若你的ui是复杂的,考虑创建一个presenter类来处理ui变化。这个可能是一个费劲的工作,但他会使你的UI组件更容易测试
- 避免饮用一个View或者Activity context在你的ViewModel中。若是ViewModel比activity(例如配置变化)活得久,你的acitivity会泄漏并且不会被垃圾收集器合适的处理。
Use cases for lifecycle-aware components
生命周期感知组件能使你在各种个样的情况下,管理生命周期更容易,一些例子如下:
- 切换粗粒度和细粒度的定位更新。使用生命周期可感知的组件能够更小粒度的抵制更新当你的应用可见,并且当应用在后台时,切换到粗粒度的更新。LiveData,一个生命周期感知组件,允许当你的用户更新地址后你的应用自动更新ui。
- 停止和开启你的video缓冲。使用生命周期可感知组件尽快开始视频缓存,但是延迟播放直到app完全start。你也能够使用生命周期可感知组件来停止缓冲当你的应用销毁后。
- 开启和关闭网络连接。使用生命周期可感知组件可以当应用在前台时,实时更新网络数据流,当应用进入后台后,自动暂停。
- 暂定和恢复动画。使用生命周期可感知组件处理当应用在后台时暂定动画,打给你应用在前台时恢复动画。
Handling on stop events
当一个Lifecycle是属于AppCompatActivity或者Fragment,在AppCompatActivity或者Fragment调用onSaveInstanceState()方法时,Lifecycle的状态会切换为CREATED并且ON_STOP事件会被分发。
当一个AppCompatActivity或者Fragment通过onSaveInstanceState()方法保存状态时,在ON_START被调用前,它的ui被认为是不允许改变的。在状态被修改后,尝试修改ui可能造成你的应用导航状态矛盾,这也是为什么在状态被保存后,若是应用运行FragmentTransaction,FragmentManager会抛出异常。详情查看commit()。
若是和Lifecycle 关联的观察者不是处于STARTED或其之后的状态,LiveData会通过制止调用他的观察者阻止边界异常情况。在后台,决定调用他的观察者之前,LiveData会调用isAtLeast() 方法判断。
不幸的是,AppCompatActivity的onStop() 方法被调用在onSaveInstanceState()之后。这就会留个间隙:当ui状态不允许改变但是Lifecycle 还没有切换到CREATED状态。
为了阻止这种情况,在beta2版本的Lifecycle类或者低版本会将状态标志为 CREATED但是不分发事件,这样任何代码核对当前的状态都将获取到真实的状态,尽管这个事件不会被分发,直到onStop方法被系统调用。
不幸的是,这种情况会导致两个主要问题:
在api23和更低的版本里,Android系统事实航会保存activity的状态若是他的部分被其他activity覆盖。换句话说,系统将会调用onSaveInstanceState()方法,但是不会调用onStop方法,这样可能会造成长事件的间隔当观察者认为lifecycle是active即使ui不能改变。
任何类想要向LiveData暴露Behavior,必须实现这种beta2或者更低版本的Lifecycle提供的措施。
备注:为了使传播更简单,以及为旧版本提供更好的兼容性,从1.0.0-rc1版本开始,当onSaveInstanceState()方法被调用后,不再等待onStop()方法被调用,lifecycle对象会被标记为CREATED和ON_STOP被分发。这可能不回影响你的代码,但有时你需要了解到它并不符合Activity的调用顺序在api26或者更低的版本。