Android Lifecycle、ViewModel和LiveData

1、概述

在I / O '17的时候,其中一个重要的主题是Architecture Components。这是一个官方的新库。旨在帮助开发者设计“健壮,可测试和可维护”的应用程序。简而言之,这个库可以帮助开发者更好地处理生命周期事件和配置更改时的数据持久性,同时还能帮助开发者创建更好的体系结构应用程序,并避免难以维护和测试的膨胀类。

其官方文档在这里:Android Architecture Components

这里我打算详细讨论下Architecture Components里面的Lifecycle、ViewModel和LiveData这3个部分。先看一下这三者的类和相关类的关系图:

关系图

粗一看有些复杂,接下来会详细分析下各个部分负责的内容。

2、Lifecycle

根据官方文档,Lifecycle是一个抽象类,一个有Android 生命周期的对象附在它上面, 并且它持该对象的当前生命周期所处状态,所以其他对象可以观察到这种状态并做出相应的反应。为了跟踪这种状态,Lifecycle类包含两个枚举类Event和State。

2.1 Event

一个Event代表当Android 生命周期的对象的生命周期发生改变时候,会触发的一个生命周期事件(例如一个activity正在被恢复)。在LifecycleObserver类中,可以为每个事件实现回调,这样在生命周期的对象的生命周期改变的时候我们能进行相关的处理。arch.lifecycle包提供了Annotation,这意味着可以在类中注释应该在某些生命周期事件中触发的方法。比如像下面这样:


@OnLifecycleEvent(ON_STOP)

void doSometing(){

}

还可以在同一个带注释的方法中处理多个生命周期事件:


@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方法

上图中看到可以Lifecycle的核心方法主要用来管理观察者:

① addObserver():该方法用来添加一个Observer实例,只要LifecycleOwner改变状态就会通知它。当添加一个Observer时,它将接收导致当前状态的所有事件。举例来说,如果Lifecycle目前在RESUMED状态,则观察员将收到ON_CREATE,ON_START和ON_RESUME事件。

② removeObserver():可以调用此方法从Lifecycle的观察者列表中删除给定的观察者。从生命周期中删除观察者将不再接收任何触发事件。

③ getCurrentState():返回生命周期所在的当前状态。

前面提到了一个和Lifecycle息息相关的类LifecycleOwner,接下来看一下这个类是干什么的。

2.4 LifecycleOwner

这个其实是一个接口类,里面只有一个方法getLifecycle()


public interface LifecycleOwner {

    @NonNull

    Lifecycle getLifecycle();

}

里面的唯一方法getLifecycle()就是用来返回Lifecycle的。而这个方法的所代表的意思很简单,告诉要使用Lifecycle的组件,我是一个生命周期感知组件,我存在生命周期的概念。我们现在看v4组件下的Activity,会继承自SupportActivity,和Fragment,这两个类都实现了LifecycleOwner这个接口。同时这两个类有一个LifecycleRegistry属性,这个属性就是继承自Lifecycle而LifecycleOwner的getLifecycle()返回的就是这个LifecycleRegistry。这样就可以通过这个方法获取该Activity的Lifecycle,再通过对Lifecycle的观察对Activity的生命周期进行观察,如下:


class MainActivity extends AppCompatActivity() {

   protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState)

        getLifecycle().addObserver(new SomeObserver())

    }

}

SomeObserver类继承自LifecycleObserver,如下所示


class SomeObserver implements LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)

        void MyResume() {

        }

        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)

        void MyPause() {

        }

    }

这样的话,当MainActivity 触发相应的事件,SomeObserver 就会回调相应的方法。

Lifecycle、LifecycleOwner、LifecycleObserver 三者关系

到此Lifecycle相关部分介绍的差不多了,但是还不够,官方还提供了其他已经封装好的能感知Lifecycle组件生命周期的配套组件LiveData和ViewModels。

3 LiveData

LiveData的作用是持有一份给定的数据,并且能够在生命周期变化中观察它。该类为开发者提供了一系列方法,方便对这个类持有数据的观察者的管理。LiveData会根据观察者绑定的LifecycleOwner的生命周期情况,来决定是否将数据改变的情况通知给观察者。这么做的好处就是,比如在一个Activity里面请求了网络更新这个Activity A界面下的数据,但是如果数据还没有请求回来这时候用户跳转到了另一个Activity B,这时候如果这个数据是被LiveData所持有的,那么这个被网络请求更新的数据就不会通知给Activity A。

LiveData的处理逻辑如下图所示:

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 更改时自动保留数据。

ViewModel生命周期

官方给的建议是将LiveData和ViewModel配合来使用。当不需要ViewModel时(比如Activity调用finish()方法),ViewModel会回调onCleared()方法,之后会销毁自己。这一好处也是避免了内存泄漏的情况发生。

Activity正常销毁时的ViewModel

例子如下:


public class MyViewModel extends ViewModel {

    private MutableLiveDatam<Integer> mValue= new MutableLiveData<>(); 

    public LiveData getValue(){

        return  mValue;

    }

    public void setValue(Integer value ){

        mValue.setValue(value);

    }

}

Tips: ViewModel可能比它所涉及的一些Activity/Fragment实例存活时间更长。因此不要保留 Activity的Context和View相关的任何引用,不然可能引起内存泄漏。如果想引用Application的Context,可以使用AndroidViewModel,可以通过其getApplication()来获取。

4.2 ViewModel使用

ViewModel的创建不能通过简单的new对象来进行。需要通过activity / fragment 获取ViewModel实例。为此,需要访问一个名为ViewModelProviders的辅助类 ,通过这样获取的ViewModel对于一个activity 只有一份:


MyViewModel mMyViewModel  = ViewModelProviders.of(getActivity()).get(MyViewModel .class);

ViewModelProviders这个类,本质上其实是一个工厂类。这个类内部包含了一个ViewModelStore实例,它负责存储创建的ViewModels。同时可以使用ViewModelProvider的get()方法来获取作为参数传入的ViewModel类型的实例。该方法源码如下:


@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 对象并存入ViewModelStore。

    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的膨胀,使各个类的职责尽可能单一。

④ 容易测试:职责的分离会使测试这些职责更容易,而且还可以产生更细粒度的测试用例。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容