预防小贴士
保持基本的手部和呼吸道卫生,坚持安全饮食习惯,并尽可能避免与任何表现出有呼吸道疾病症状(如咳嗽和打喷嚏等)的人密切接触。
1、概述
在I / O '17的时候,其中一个重要的主题是Architecture Components。这是一个官方的新库。旨在帮助开发者设计“健壮,可测试和可维护”的应用程序。简而言之,这个库可以帮助开发者更好地处理生命周期事件和配置更改时的数据持久性,同时还能帮助开发者创建更好的体系结构应用程序,并避免难以维护和测试的膨胀类。
其官方文档在这里:https://developer.android.google.cn/topic/libraries/architecture/
这里我打算详细讨论下Architecture Components里面的Lifecycle、ViewModel和LiveData这3个部分。先看一下这三者的类和相关类的关系图:
粗一看有些复杂,接下来会详细分析下各个部分负责的内容。
2、Lifecycle
根据官方文档,Lifecycle是一个抽象类,一个有Android 生命周期的对象附在它上面, 并且它持该对象的当前生命周期所处状态,所以其他对象可以观察到这种状态并做出相应的反应。为了跟踪这种状态,Lifecycle类包含两个枚举类Event和State。
2.1 Event
一个Event代表当Android 生命周期的对象的生命周期发生改变时候,会触发的一个生命周期事件(例如一个activity正在被恢复)。在LifecycleObserver类中,可以为每个事件实现回调,这样在生命周期的对象的生命周期改变的时候我们能进行相关的处理。arch.lifecycle包提供了Annotation,这意味着可以在类中注释应该在某些生命周期事件中触发的方法。比如像下面这样:
代码语言:javascript复制@OnLifecycleEvent(ON_STOP)
void doSometing(){
}
还可以在同一个带注释的方法中处理多个生命周期事件:
代码语言:javascript复制@OnLifecycleEvent({ON_STOP,ON_START})
void doSometing(){
}
下面是各种事件的情况:
① ON_ANY:在任何生命周期事件时触发。
② ON_CREATE:创建LifecycleOwner(下面会讲这个类)时将触发此事件。
③ ON_DESTROY:LifecycleOwner被销毁时将触发此事件。
④ ON_PAUSE:LifecycleOwner暂停时将触发此事件。
⑤ ON_RESUME:在LifecycleOwner恢复时触发此事件。
⑥ ON_START:启动LifecycleOwner时触发此事件。
⑦ ON_STOP:LifecycleOwner停止时触发此事件。
ON_CREATE,ON_START,ON_RESUME的方法会在LifeCycleOwner对应方法(onCreate()、onStart()、onResume())返回后被调用。生命周期事件ON_DESTROY, ON_STOP, ON_PAUSE的方法会在LifeCycleOwner对应方法(onDestory()、onStop()、onPause()被调用之前调用。
2.2 State
生命周期的State本质上是介于两个生命周期事件之间的一种情况。触发事件后,生命周期将进入一个状态,然后在触发另一个事件时离开该状态并进入另一个状态。如下图所示:
从上图可以看到,Lifecycle实例在任意时间段里肯定是下面五个状态的其中一种:
① INITIALISED:初始起点的生命周期状态。
② CREATED:ON_CREATE事件之后,ON_START事件之前的状态。或者是ON_STOP事件之后,ON_DESTROY事件之前的状态。
③ STARTED:ON_START事件之后,ON_RESUME事件之前的状态。或者是事件之ON_PAUSE后,ON_STOP事件之前的状态。
④ RESUMED:ON_RESUME事件之后的状态。
⑤ DESTROYED:ON_DESTROY事件之后的状态。
如果想获取Lifecycle实例的当前状态,那么可以调用getCurrentState()方法,该方法允许开发者在任何时间检索其当前状态。这有助于在执行某种形式的操作之前检查Lifecycle组件的状态。State对象还可以调用isAtLeast()方法来判断当前状态是否大于等于给定状态。
2.3 Lifecycle相关方法
了解了Lifecycle所包含的State和Event枚举类,现在看看Lifecycle和其它类进行交互的一些方法。
上图中看到可以Lifecycle的核心方法主要用来管理观察者:
① addObserver():该方法用来添加一个Observer实例,只要LifecycleOwner改变状态就会通知它。当添加一个Observer时,它将接收导致当前状态的所有事件。举例来说,如果Lifecycle目前在RESUMED状态,则观察员将收到ON_CREATE,ON_START和ON_RESUME事件。
② removeObserver():可以调用此方法从Lifecycle的观察者列表中删除给定的观察者。从生命周期中删除观察者将不再接收任何触发事件。
③ getCurrentState():返回生命周期所在的当前状态。
前面提到了一个和Lifecycle息息相关的类LifecycleOwner,接下来看一下这个类是干什么的。
2.4 LifecycleOwner
这个其实是一个接口类,里面只有一个方法getLifecycle()
代码语言:javascript复制public interface LifecycleOwner {
@NonNull
Lifecycle getLifecycle();
}
里面的唯一方法getLifecycle()就是用来返回Lifecycle的。而这个方法的所代表的意思很简单,告诉要使用Lifecycle的组件,我是一个生命周期感知组件,我存在生命周期的概念。我们现在看v4组件下的Activity,会继承自SupportActivity,和Fragment,也就是说,你现在使用的Activity和Fragment这两个类官方都实现了LifecycleOwner这个接口。同时这两个类有一个LifecycleRegistry属性,这个属性就是继承自Lifecycle而LifecycleOwner的getLifecycle()返回的就是这个LifecycleRegistry。这样就可以通过这个方法获取该Activity的Lifecycle,再通过对Lifecycle的观察对Activity的生命周期进行观察,如下:
代码语言:javascript复制class MainActivity extends AppCompatActivity() {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState)
getLifecycle().addObserver(new SomeObserver())
}
}
SomeObserver类继承自LifecycleObserver,如下所示
代码语言:javascript复制class SomeObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void MyResume() {
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void MyPause() {
}
}
这样的话,当MainActivity 触发相应的事件,SomeObserver 就会回调相应的方法。
到此Lifecycle相关部分介绍的差不多了,但是还不够,官方还提供了其他已经封装好的能感知Lifecycle组件生命周期的配套组件LiveData和ViewModels。
3、 LiveData
LiveData的作用是持有一份给定的数据,并且能够在生命周期变化中观察它。该类为开发者提供了一系列方法,方便对这个类持有数据的观察者的管理。LiveData会根据观察者绑定的LifecycleOwner的生命周期情况,来决定是否将数据改变的情况通知给观察者。这么做的好处就是,比如在一个Activity里面请求了网络更新这个Activity A界面下的数据,但是如果数据还没有请求回来这时候用户跳转到了另一个Activity B,这时候如果这个数据是被LiveData所持有的,那么这个被网络请求更新的数据就不会通知给Activity A。
LiveData的处理逻辑如下图所示:
3.1 setValue()
当调用LiveData的setValue(T value)方法时,将设置LiveData持有的数据。判断是否有活跃的观察者,这里的活跃观察者指观察者绑定的LifecycleOwner中的Lifecycle处于STARTED和RESUMED状态的观察者,如果有的话,将更新的数据发送给这些处于活跃状态的观察者。
3.2 observe()
当调用的LiveData的observe(LifecycleOwner owner, Observer observer)方法时,根据情况会有不同的处理。如果LifecycleOwner处于DESTROYED状态,那么这个调用将被完全忽略。如果它不是DESTROYED,那么此时观察者Observer将被添加到LiveData的观察者列表中,同时该观察者会LifecycleOwner绑定,如果LifecycleOwner的生命周期状态变成DESTROYED,那么和这个LifecycleOwner绑定的观察者会自动被移除,这么做的好处就是可以避免很多可能会出现的内存泄漏。同时如果之前LiveData已经被设置过数据了,那么观察者会立刻接收到最新的数据。
如果之前LiveData没有观察者观察它,那第一次接受观察者会回调LiveData的onActive()方法。这个方法里面可以执行一些数据拉取操作,使数据处于处于最新状态,回调这个方法意味着该LiveData正在被使用中。
3.3 removeObserver()
这个方法有两个重载方法 removeObserver(Observer observer)和 removeObservers(LifecycleOwner owner) 一个是移除观察者,一个是移除和该LifecycleOwner所绑定的所有观察者。
如果LiveData的观察者列表中不存在活跃观察者了,也就是说没有一个观察者绑定的LifecycleOwner的Lifecycle处于 STARTED 或者RESUMED状态。这时候LiveData会回调onInactive()。这时候就算其持有的数据更新了,也不会发起通知。
3.4 其他方法
① hasActiveObservers():检查LiveData中是否有活跃的观察者。
② hasObservers():检查LiveData中是否有观察者。
③ observeForever(Observer observer):用于将一个Observer添加到一个活跃列表中,该列表将始终保持ACTIVE状态,因此永远不会自动从Observer实例列表中移除它。要移除此Observer时必须手动调用removeObserver()。
④ postValue(T value):在子线程中给LiveData设置值。
Tips:LiveData中的setValue(T value)、postValue(T value)都是protected,也就是说只能自己或者它的继承类进行调用。如果想在外面调用这些方法可以使用它的继承类MutableLiveData。
4、ViewModel
4.1 ViewModel实现
ViewModel这个类设计出来的目的是为了解决configuration 改变时候,Activity会重建,这时候里面的数据不易保存的问题。有了它Activity或者Fragment就可以不需要承担保留数据的责任了,可以把这些事情交给ViewModel,做到很好的数据和视图的解耦。同时ViewModel会在configuration 更改时自动保留数据。
官方给的建议是将LiveData和ViewModel配合来使用。当不需要ViewModel时(比如Activity调用finish()方法),ViewModel会回调onCleared()方法,之后会销毁自己。这一好处也是避免了内存泄漏的情况发生。
例子如下:
代码语言:javascript复制public class MyViewModel extends ViewModel {
private MutableLiveDatam<Integer> mValue= new MutableLiveData<>();
public LiveData getValue(){
return mValue;
}
public void setValue(Integer value ){
mValue.setValue(value);
}
}
代码语言:javascript复制
Tips: ViewModel可能比它所涉及的一些Activity/Fragment实例存活时间更长。因此不要保留 Activity的Context和View相关的任何引用,不然可能引起内存泄漏。如果想引用Application的Context,可以使用AndroidViewModel,可以通过其getApplication()来获取。
4.2 ViewModel使用
ViewModel的创建不能通过简单的new对象来进行。需要通过activity / fragment 获取ViewModel实例。为此,需要访问一个名为ViewModelProviders的辅助类 ,通过这样获取的ViewModel对于一个activity / fragment只有一份:
代码语言:javascript复制
代码语言:javascript复制MyViewModel mMyViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel .class);
ViewModelProviders这个类,本质上其实是一个工厂类。这个类内部包含了一个ViewModelStore实例,它负责存储创建的ViewModels。同时可以使用ViewModelProvider的get()方法来获取作为参数传入的ViewModel类型的实例。该方法源码如下:
代码语言:javascript复制
代码语言:javascript复制@NonNull
@MainThreadpublicT get(@NonNull String key, @NonNull Class modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
if (viewModel != null) {
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
如源码所示,当调用此get()方法时,ViewModelProvider将检查ViewModelStore是否已具有该类类型的现有ViewModel,如果是,则将返回它。但是,如果不存在,那么将创建一个新的ViewModel并将其添加到ViewModelStore中。
获取到ViewModel 就可以使用里面的属性和方法来进行操作了。下面说明下ViewModel存在的意义。
4.3 ViewModel 意义
ViewModel 被设计出来,不仅为了解决上面所说的configuration改变时候能保留数据。其真正意义在于以下几个方面:
① 职责分离:使Activity/Fragment不用再负责从某些数据源获取数据,只需要负责展示数据就好,同时还消除了在配置更改时保留数据对象实例的引用的责任。这两个职责都转给了ViewModel。
② 简化对没用数据的清理:当Activity/Fragment负责清理数据的操作时,需要使用大量代码来清理这些请求。但是将这些清理操作放到ViewModels onCleared()方法中,这些资源在Activity结束时会自动清除。
③ 减少类的膨胀:由于职责的转移,Activity/Fragment删除了许多用于处理请求,状态持久性和注销数据的代码。这些代码通常会导致Activity/Fragment变得非常臃肿,这样的代码会难以扩展和维护。使用ViewModels可以帮助开发者缓解Activity/Fragment的膨胀,使各个类的职责尽可能单一。
④ 容易测试:职责的分离会使测试这些职责更容易,而且还可以产生更细粒度的测试用例。