MVVM-LiveData[Kotlin 项目实战]

前言

  废话少说,直接上点干货。https://github.com/yiweituoxie/MyKotlin ,代码结构如下图

project.png

效果图.gif

  这是一个简单介绍在MVVM框架下使用RxJava2+Retrofit2请求网络数据的小项目,接下来我会不断地在这个项目上更新一些平时用到的好用的特性或者功能,同时也会发布对应的文章。首先要声明的是,这篇文章和这个项目暂时是针对MVVM的初学者或者刚开始使用MVVM的同学的,所以有大篇幅都在介绍官方的或者是针对项目的代码进行说明。
  平常用习惯MVP的同学也可以在我的github上找到一个相对比较成熟的MVP项目,欢迎交流与push https://github.com/yiweituoxie/Mvp-Kotlin
  想要更加详细地了解MVVM或者MVP的同学可以转弯到官方https://github.com/googlesamples/android-architecture?utm_source=tuicool
官方的Sample中不但介绍了MVVM的两种实现(LiveData,DataBinding),同时也向开发者提供了一些非常好用的类,例如SingleLiveEvent。

MVVM剧情介绍

  为了不落俗套,我在这边就跳过故事的背景介绍,想要了解MVVM框架的可以移步到https://www.zhihu.com/question/30976423/answer/50224601。故事的梗概就是记述我们的大侦探夏洛克ViewModel帮助他的两个委托人Model组织和View组织找到各自的成员在对方组织里面的另一半的故事,那如果是这样的故事,又怎么能区分这不是波洛的故事而是福尔摩斯的故事呢,对,你没猜错,华生在最新一季的MVVM中,闪亮登场了。事实上,在这一季之前,MVVM中,我们看到夏洛克ViewModel办案非常费劲,在很多观众的代码前,时常要深入到M和V里面,插手本来不属于它的事务,这也让很多新来的观众一头雾水,说好的解耦呢,说好的分离呢,说好的效率呢。对MVVM的口碑可以说分歧很大,有不少新来的观众纷纷弃剧。在收视率的压力下,导演兼编剧Google推出新角色华生LiveData可以说是众望所归。
  网上有相当多介绍LiveData和ViewModel的文章,可以说是五花八门琳琅满目,其中不乏优秀的影评,但是很多新老观众总结起来都表示挺晕的,其实不妨看看导演自己怎么介绍角色。在Google的官网上对LiveData有详细的介绍:

LiveData.png

  LiveData属于android.arch.lifecycle:livedata-core这个包下面的一个类,包名已经很显然地表明,LiveData本身是和内存回收有关的。第一句话,LiveData是一个可以在给定生命周期内观察到的数据持有者类,没错,非常官方,后面紧接着是LiveData的内部原理,其实翻译成好理解一点的话(并不严谨),就是LiveData可以绑定多个观察者,并且会跟随观察者的生命周期存活,当活动观察者的数量在0和1之间发生变化时,可以得到通知。而在最后一段,则直截了当地说,它和ViewModel的亲密关系。
  翻开ViewModel的介绍,作为一个大名鼎鼎的Class,竟然在自己的个人空间里面,直接了当得摆明了和LiveData关系,可以说是男友力Max:

ViewModel.png

  “ViewModel通常通过LiveData或Android DataBinding公开这些信息”,事实上,有相当多的人还在使用AndroidDataBinding,而用过的人,我相信都或多或少感受到它的不方便,对于有代码洁癖的小伙伴来说,设置有点膈应。所以照顾到大家得感受,官网只贴出了ViewModel和LiveData的合影,作为官方认证的CP,观众还要说啥,高举起来,舔就完事了。

public class UserActivity extends Activity {

     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.user_activity_layout);
         final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
         viewModel.userLiveData.observer(this, new Observer() {
            @Override
             public void onChanged(@Nullable User data) {
                 // update ui.
             }
         });
         findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                  viewModel.doAction();
             }
         });
     }
 }
public class UserModel extends ViewModel {
     public final LiveData<User> userLiveData = new LiveData<>();

     public UserModel() {
         // trigger user load.
     }

     void doAction() {
         // depending on the action, do necessary business logic calls and update the
         // userLiveData.
     }
 }

  上面的代码可以说是相当地精简,在UserActivity中,viewModel的userLiveData绑定到UserActivity上,当调用可以发现viewModel的doAction方法改变userLiveData的值是,userLiveData会自动通知它的观察者(也就是UserActivity):“我变了,你要不要做点事情”。可以看到,ViewModel里是并没有View的任何逻辑或对象方法,而是通过定义LiveData,通知LiveData的观察者。相信你很容易就能推理得出,开发者可以在任意的View(界面)上使用UserModel,比如说登录界面,注册界面,用户管理界面等等,只需要监听界面关心的数据就可以了。这也就是ViewModel的复用性和低耦合的优点。

我的小剧场

  小剧场是一个简单的登录界面,麻雀虽小,但五脏俱全,开篇的时候已经介绍过了,代码是基于RxJava2+Retrofit2进行网络请求的,正是如此,ViewModel需要通知View的信息就包括,正在请求,请求成功和请求失败三种信息。所以在ViewModel中,定义了三个LiveData,分别是userInfo,loadingMessage,toastStringMessage,分别保存请求成功的用户信息,是否正在请求,异常信息,并且在对应的方法中修改它们的值。


LoginViewModel.png

  对应的,我们需要在View(LoginActivity)中创建一个ViewModel的实例,当然少不了的,就是对关心的LiveData绑定观察者,也就是View本身。


LoginActivity.png

  为什么要一再强调是关心的LiveData呢?这也正是上面提到过的复用性和低耦合了,举个简单的例子,在用户管理界面,使用同样一个UserModel进行数据的获取,但是这个时候加载用户的信息,首先会获取本地保存的信息,同时为了保证数据的一致性,还会获取服务器的用户信息(比如在其它地方修改过用户的信息)。出于对UX的考虑,等待框是不会出现的,只需要默默地同步信息就可以了。那么,在用户管理界面,就并不需要监听loadingMessage。
   这其实也侧面地说明了,View和ViewModel的关系是多对多的,也就是View中可以有多个ViewModel,ViewModel也可以被多个View绑定。

多说两句

  细心的同学可能已经留意到同样是被监听的数据,loadingMessage 继承自MutableLiveData,而 toastStringMessage则继承前言提到过的 SingleLiveEvent。这究竟是出于什么考虑呢?

class LoadingMessage : MutableLiveData<Boolean>()
class ToastStringMessage : SingleLiveEvent<String>() {
    override fun setValue(t: String?) {
        super.setValue(t)
        super.setValue(null)
    }

    override fun postValue(t: String?) {
        super.postValue(t)
        super.postValue(null)
    }
}

  这里就涉及到更加规范的ViewModel和LiveData的使用,有兴趣的同学可以看一下官方的建议,https://medium.com/androiddevelopers/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54,作者提出了更多的使用建议,相信看完以后,你会对LiveData有更深的认识。中文翻译在https://zhuanlan.zhihu.com/p/33206893
  Chow Chow &C U Next Time

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

推荐阅读更多精彩内容