前言
废话少说,直接上点干货。https://github.com/yiweituoxie/MyKotlin ,代码结构如下图
这是一个简单介绍在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属于android.arch.lifecycle:livedata-core这个包下面的一个类,包名已经很显然地表明,LiveData本身是和内存回收有关的。第一句话,LiveData是一个可以在给定生命周期内观察到的数据持有者类,没错,非常官方,后面紧接着是LiveData的内部原理,其实翻译成好理解一点的话(并不严谨),就是LiveData可以绑定多个观察者,并且会跟随观察者的生命周期存活,当活动观察者的数量在0和1之间发生变化时,可以得到通知。而在最后一段,则直截了当地说,它和ViewModel的亲密关系。
翻开ViewModel的介绍,作为一个大名鼎鼎的Class,竟然在自己的个人空间里面,直接了当得摆明了和LiveData关系,可以说是男友力Max:
“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,分别保存请求成功的用户信息,是否正在请求,异常信息,并且在对应的方法中修改它们的值。
对应的,我们需要在View(LoginActivity)中创建一个ViewModel的实例,当然少不了的,就是对关心的LiveData绑定观察者,也就是View本身。
为什么要一再强调是关心的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