有了整体的认识,就可以对之前没有详细介绍的类做一个深入的探究。首先来看看Lifecycle。
Handling Lifecycles
android.arch.lifecycle提供的类和接口可以让你构建能感知生命周期(lifecycle-aware)的类。所谓可以感知生命周期就是能够根据Activity或者Fragment的生命周期自行调整类的行为。
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;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, (location) -> {
// update UI
});
}
public void onStart() {
super.onStart();
myLocationListener.start();
}
public void onStop() {
super.onStop();
myLocationListener.stop();
}
}
在Activity的onCreate()方法中创建Listener
,在onStart()
时开始监听,在onStop()
中停止监听。看起来一切正常啊,就是可能Activty不停的切换会调用多次LocationListener的onStart
和onStop
而已。
但是问题来了,举个例子哈。比如有些App会提供一个选项,说允不允许提供位置,那么在定位之前就需要检查这些设置。好,那么就在Activity里提供检查的代码:
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, location -> {
// update UI
});
}
public void onStart() {
super.onStart();
Util.checkUserStatus(result -> {
// what if this callback is invoked AFTER activity is stopped?
if (result) {
myLocationListener.start();
}
});
}
public void onStop() {
super.onStop();
myLocationListener.stop();
}
}
看起来好像也没有问题呀,让我们来一起分析一下这段代码。假如程序执行到super.onStart()
这里还没等checkUserStatus执行完成就执行了onStop()
(这里就假设检查本身回花费时间比较长吧),意识到出了什么问题了吗?myLocationListener
的stop()
方法在start()
之前执行了,这本身在逻辑上就是不合理的。更严重的问题是:myLocationListener
过一段时间开始执行start()
,然后没有stop的代码被调用了。这意味着如果没有其他操作,Activity将会一直在后台监听Location的改变。
Lifecycle就是用来解决这些问题的,并且解决的非常优雅(in a resilient and isolated way)。
Lifecycle
Lifecycle是一个包含组件(Activity或者Fragment)生命周期状态的类,这个类还能够为其他类提供当前的生命周期。
Lifecycle使用两个主要的枚举来跟踪他所关联组件的生命周期。
- Event 事件 从组件或者Lifecycle类分发出来的生命周期,它们和Activity/Fragment生命周期的事件一一对应。(ON_CREATE,ON_START,ON_RESUME,ON_PAUSE,ON_STOP,ON_DESTROY)
- State 状态 当前组件的生命周期状态(INITIALIZED,DESTROYED,CREATED,STARTED,RESUMED)
说的有点抽象了,看一下下面的图:
具体的代码如下:
public class MyObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
}
}
//aLifecycleOwner一般是实现了LifecycleOwner的类,比如Activity/Fragment
aLifecycleOwner.getLifecycle().addObserver(new MyObserver());
LifecycleOwner
那什么是LifecycleOwner呢?实现LifecycleOwner就表示这是个有生命周期的类,他有一个getLifecycle ()方法是必须实现的。com.android.support:appcompat-v7:26.1.0
中的AppCompatActivity已经实现了这个接口,详细的实现可以自行查看代码。
对于前面提到的监听位置的例子。可以把MyLocationListener
实现LifecycleObserver,然后在Lifecycle
(Activity/Fragment)的onCreate
方法中初始化。这样MyLocationListener
就能自行处理生命周期带来的问题。具体代码如下:
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();
}
});
}
}
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.getState().isAtLeast(STARTED)) {
// connect if not connected
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop() {
// disconnect if connected
}
}
简单过一下上面的代码不难发现这样可以解决文章开始提到的问题。(调用enable()时,已经处于CREATED状态,下面的代码是不会执行的)这样在如果Lifecycle没有处在合适的时机是不会调用相关回调的。现在MyLocationListener
是有生命周期感知能力的(lifecycle-aware),不再依赖Activity的生命周期。
Lifecycles的最佳建议
- 保持UI Controllers(Activity/Fragment)中代码足够简洁。一定不能包含如何获取数据的代码,要通过ViewModel获取LiveData形式的数据。
- 用数据驱动UI,UI的职责就是根据数据改变显示的内容,并且把用户操作UI的行为传递给ViewModel。
- 把业务逻辑相关的代码放到ViewModel中,把ViewModel看成是链接UI和App其他部分的胶水。但ViewModel不能直接获取数据,要通过调用其他类来获取数据。
- 使用DataBinding来简化View(布局文件)和UI Controllers(Activity/Fragment)之间的代码
- 如果布局本身太过复杂,可以考虑创建一个Presenter类来处理UI相关的改变。虽然这么做会多写很多代码,但是对于保持UI的简介和可测试性是有帮助的。
- 不要在ViewModel中持有任何View/Activity的context。否则会造成内存泄露。
附加说明
在 Support Library 26.1.0中Activity/Fragment已经实现了LifecycleOwner接口。
如果想在自定义的类中实现LifecyclerOwner,就需要用到LifecycleRegistry,并且需要自行发送Event。
相关文章:
理解Android Architecture Components系列(一)
理解Android Architecture Components系列(二)
理解Android Architecture Components系列之Lifecycle(三)
理解Android Architecture Components系列之LiveData(四)
理解Android Architecture Components系列之ViewModel(五)
理解Android Architecture Components系列之Room(六)
理解Android Architecture Components系列之Paging Library(七)
理解Android Architecture Components系列之WorkManager(八)