引入概念
-
Lifecycle解决的问题:
- 用于响应、管理其他应用组件(如
Activity
和Fragment
)的改变状态,相对于我们自己写事件监听回调接口,Lifecycle
会更加简洁、易于管理。 - 大部分应用组件都存在于
Android Framework
,生命周期绑定在此之上,并且直接由系统或者由应用进程框架管理,因此必须遵循它们的规则,避免内存泄露和应用崩溃。
- 用于响应、管理其他应用组件(如
实际场景: 我们需要在
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
}
}
- 貌似看起来很不错,但是在实际应用中,最终会存在太多用于管理其他组件生命周期状态的调用,管理多个组件时会在生命周期方法中放置大量代码,例如
onStart()
和onStop()
,这使得它们难以维护。 - 此外,无法保证组件在
Activity
或Fragment
停止之前启动,这在我们需要执行耗时长的操作时尤为真实,比如我们在onStart()
中检查某些配置,这就可能在当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: 这个事件由系统框架和
Lifecyle
类分发,并且会映射到Activit
和Fragment
的回调事件上。 - State: 代表当前
LIfecycle
对象正在处理的组件的状态。
- Event: 这个事件由系统框架和
-
通过给方法添加注解的方式可以使这个类具备监听组件生命周期的能力,然后通过
Lifecycle#addObserver()
添加观察者即可赋予其他对象这个观察能力,如下示例:// 作为观察者,我们需要实现 LifecycleObserver 接口 public class MyObserver implements LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) // 在onResume时执行 public void connectListener() { ... } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) // 在onPause时执行 public void disconnectListener() { ... } } // 添加一个观察者,使得这个观察者也可以监听组件状态变化 myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
LifeOwner
的概念
LifeOwner
只包含一个getLifecycle()
方法,用于获取Lifecycle
,使用时必须实现这个方法。如果想要管理整个应用进程的生命周期,可以使用ProcessLifecycleOwner
代替。这个接口从
Activity
和Fragment
等中抽取了Lifecycle
的所有权,并且允许编写组件来与之配合,任何自定义的应用类都可以实现LifecOwner
接口。实现了
LifecycleOwner
的组件与实现了LifecycleObserver
的组件运作方式是无缝衔接的的,因为Owner
用于提供事件,而Observer
用于注册、监听事件。
-
在前面我们定义了一个实现了
LifecycleObserver
接口的MyLocationLIstener
类,我们可以如下面代码这样在onCreate
中初始化,这意味响应生命周期变化的逻辑都提取到了MyLocationLIstener
,而不是全部挤在Activity
中,可见这样可以极大简化Activity
和Fragment
的代码逻辑。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
允许其他对象查看当前状态。
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; // 查看Lifecycle的当前状态 if (lifecycle.getCurrentState().isAtLeast(STARTED)) { // connect if not connected } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) void stop() { // disconnect if connected } }
- 通过上面实现,我们的
MyLocationListener
就完全可以管理生命周期了,如果想要在其他Activity
或Fragment
中使用它,那么只需要初始化一下就行了,其他处理操作都会在它内部处理。 - 如果一个类库提供需要结合
Android
生命周期的处理类,那么建议使用Lifecycle-aware
组件,这样的话类库客户端就可以轻易地整合这些组件而不需要手动地在客户端处理生命周期管理工作。
- 为了避免在
- 实现自定义的 LifecycleOwner
- 在
Support Library 26.1.0
以及上版本中,Fragment
和Activity
已经实现了LifecycleOwner
接口。 - 如果需要自定义实现一个
LifecycleOwner
,那么可以使用LifecycleRegistry
类,但是你需要发送事件到LifecycleRegistry
类中,如下示例:
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; } }
- 在
lifecycle-aware 组件最佳实践
尽可能保证
UI
控制器,如Activity
和Fragment
的简洁性,它们不应该请求它们自身的数据,而应交给ViewModel
去做,并观察一个LiveData
对象,用来将变化返回给UI
视图。尽量编写数据驱动型(
data-driven
)UI
,这种形式下,UI
控制器只需要负责在数据改变时更新视图,或者通知用户动作给ViewModel
将数据逻辑放到
ViewModel
类,ViewModel
应当用作UI
控制器和应用其他部分的连接器,但是注意,ViewModel
不负责请求数据(比如网络请求等),相反,它只是调用数据请求模块去请求数据,然后将数据结果返回给UI
控制器。使用
DataBinding
来维持视图与UI
控制器间的简洁性。它可以可简化视图的声明和视图更新时所需在UI
控制器中编写的代码,如果喜欢使用Java
,那么建议使用类似于ButterKnife
之类的类库来避免编写无聊的声明代码,并且它可以实现更好的抽象。如果
UI
很复杂,可以考虑创建一个Presenter
类来处理UI
更改操作,这可能很费事,但可以使UI
组件更易于测试。禁止在
ViewModel
中引用View
或者Activity
上下文(context
),否则如果ViewModel
生命周期比Activity
长时(比如configuration change
的情况),Activity
就会内存泄露而不被GC
了。
lifecycle-aware 组件使用场景
lifecycle-aware
组件可以在各种场景中让生命周期的管理更简单,比如以下场景:
- 粗略定位(
coarse-grained
)与高精度定位(fine-grained
)之间的更新状态切换。使用lifecycle-aware
组件在应用处于前台时开启高精度定位,而在后台时开启粗略定位,可以结合LiveData
来实现状态改变时更新UI
的操作。 - 开启和关闭视频缓冲。 比如使用
lifecycle-aware
组件尽快开启视频缓冲,而延迟到应用完全启动后才真正播放视频,同样也可以在应用关闭时终止缓冲动作。 - 开启和关闭网络连接。 使用
lifecycle-aware
组件进行动态更新网络数据,如应用处于前台时自动加载数据,而应用切换至后台时自动暂停加载。 - 启动和暂停
Drawable
动画。 前台时播放动画,后台是暂停动画。
处理 onStop 事件
当Lifecycle
关联到AppCompatActivity
或Fragment
时,它的状态会切换到CREATED
,而ON_STOP
状态则是会在AppCompatActivity
或Fragment
的onSaveInstanceState()
被调用是触发。
如果AppCompatActivity
或Fragment
是通过onSaveInstanceState()
中保存状态的,那么在ON_START
被调用之前,它们的UI
状态都会被认定为不可变的(immutable
)。这时如果尝试在UI
状态保存后修改UI
的话,就会导致应用导航状态不一致,这也就是为什么在状态保存后执行FragmentTransaction
,FragmentManager
会抛异常的原因了,具体看 commit()方法。
如果LiveData
的已经关联到Lifecycle
的Observer
还没到到达STARTED
状态的话, LiveData
可以通过终止observer
的调用来避免上述边角情况的发生,这是因为LiveData
会在执行Observer
之前先调用isAtLeast()
确定状态,然后再决定是否执行。
然而不幸的是,AppCompatActivity
的onStop()
方法实在onSaveInstanceState()
之后调用的,这种情况就导致已经保存的UI
状态不允许改变,而Lifecycle
又还没有到达STARTED
状态。
为了避免这个问题的发生,在版本beta2
及之前的Lifecycle
类都会将这一状态标记为CREATED
,而不分发这一事件,这样,任何检查当前状态的代码都能拿到真实状态值,即使这一事件还没有被分发,直到系统调用onStop()
方法。
然而又不幸的是,这个解决方案有两大问题:
- 在
API 23
及之前的版本,Android
系统确实会保存Activity
的状态,即使是由其他AActivity
转换的部分,也就是说,系统调用onSaveInstanceState()
,但是确实没有调用onStop()
必要。这造成了一个潜在的长间隔期,而在这个间隔期之间,observer
一直会认为Lifecycle
是活动的,即使UI
状态已经不能被改变了。 - 任何想要暴露给
LiveData
类似行为的类都必须实现Lifecycle
在beta2
及之前版本所提供的解决方案。
Note: 为了简化流程并兼容老版本,请直接从版本1.0.0-rc1
开始使用,Lifecycle
对象会被标记为CREATED
,并且会在onSaveInstanceState()
被调用是标记为ON_STOP
状态,而无需等待onStop()
的调用。虽然这并不会影响我们的代码,但是确是我们需要注意的,因为它没有遵循API 26
及以前版本中Activity
的生命周期调用次序