WHAT?
mvvm is ?
- 先来张图吧!
图片来自csdn某个老哥时间久远找不到作者了如果侵权了可以私信我我删掉自己画一哈(懒死我了)
- 什么是mvvm?
很简单,Model(数据)View(视图)ViewModel(数据视图管理器) - 能不能具体点呢?
Model:bean(实体类)、Data Source(Http请求相关、数据库相关)
View:xml、View、Activity、Fragment 等UI相关
ViewModel:先简单理解为管理器
mvc mvp ?
- mvc
-
- 该MVC框架虽然也是把代码逻辑和UI层分离,但是View层能做的事情还是很少的,很多对于页面的呈现还是交由C实现,这样会导致项目中C的代码臃肿,如果项目小,代码臃肿点还是能接受的,但是随着项目的不断迭代,代码量的增加。
- M-Model : 业务逻辑和实体模型(biz/bean)
V-View : 布局文件(XML)
C-Controller : 控制器(Activity) - mvp
-
- 该MVP框架相对于MVC框架做了较大的改变,将Activity当做View使用,代替MVC框架中的C的是P,对比MVC和MVP的模型图可以发现变化最大的是View层和Model层不在直接通信,所有交互的工作都交由Presenter层来解决。既然两者都通过Presenter来通信,那么不难理解MVP框架给予接口设计的理念就是为了复用和可扩展性。
WHY?
优点
- 数据驱动
在MVVM中,以前开发模式中必须先处理业务数据,然后根据的数据变化,去获取UI的引用然后更新UI,通过也是通过UI来获取用户输入,而在MVVM中,数据和业务逻辑处于一个独立的ViewModel中,ViewModel只要关注数据和业务逻辑,不需要和UI或者控件打交道。由数据自动去驱动UI去自动更新UI,UI的改变又同时自动反馈到数据,数据成为主导因素,这样使得在业务逻辑处理只要关心数据,方便而且简单很多。 - 低耦合度
MVVM模式中,数据是独立于UI的,ViewModel只负责处理和提供数据,UI想怎么处理数据都由UI自己决定,ViewModel 不涉及任何和UI相关的事也不持有UI控件的引用,即使控件改变(TextView换成EditText)ViewModel 几乎不需要更改任何代码,专注自己的数据处理就可以了,如果是MVP遇到UI更改,就可能需要改变获取UI的方式,改变更新UI的接口,改变从UI上获取输入的代码,可能还需要更改访问UI对象的属性代码等等。 - 更新 UI
在MVVM中,我们可以在工作线程中直接修改ViewModel的数据(只要数据是线程安全的),剩下的数据绑定框架帮你搞定,很多事情都不需要你去关心。 - 团队协作
MVVM的分工是非常明显的,由于View和ViewModel之间是松散耦合的。一个是处理业务和数据,一个是专门的UI处理。完全有两个人分工来做,一个做UI(xml 和 Activity)一个写ViewModel,效率更高。 - 可复用性
一个ViewModel复用到多个View中,同样的一份数据,用不同的UI去做展示,对于版本迭代频繁的UI改动,只要更换View层就行,对于如果想在UI上的做AbTest 更是方便的多。 - 单元测试
ViewModel里面是数据和业务逻辑,View中关注的是UI,这样的做测试是很方便的,完全没有彼此的依赖,不管是UI的单元测试还是业务逻辑的单元测试,都是低耦合的。
缺点
- 代码量增多
- 代码变复杂,交接难度增加
- 巨型ViewModel的出现
HOW?
使用Android Architecture Components(aac)框架
- 什么是aac框架
- Architecture Components是在2017年GoogleI/O大会上,Google官方推出的一个构建Android应用架构的库。它可以帮你避免在Android应用开发中常见的一些问题,比如:内存泄露,管理组件生命周期等等。
- ViewModel 负责调用 Model(可以称之为数据源),拿到结果后,更新自身。而 View 与 ViewModel 双向绑定(后面会讲怎么实现绑定的),所以 View 就会自动更新。这就是 MVVM 大致的思想。
-
- Lifecycle
生命周期管理,把原先Android生命周期的中的代码抽取出来,如将原先需要在onStart()等生命周期中执行的代码分离到Activity或者Fragment之外。 - LiveData
一个数据持有类,持有数据并且这个数据可以被观察被监听,和其他Observer不同的是,它是和Lifecycle是绑定的,在生命周期内使用有效,减少内存泄露和引用问题。 - ViewModel
用于实现架构中的ViewModel,同时是与Lifecycle绑定的,使用者无需担心生命周期。可以在多个Fragment之间共享数据,比如旋转屏幕后Activity会重新create,这时候使用ViewModel还是之前的数据,不需要再次请求网络数据。 - Room
谷歌推出的一个Sqlite ORM库,不过使用起来还不错,使用注解,极大简化数据库的操作,有点类似Retrofit的风格。
- Lifecycle
- 为什么使用aac框架
- 应用开发者所面对的问题
移动设备资源有限,任何时候系统都有可能杀死应用释放内存,这样的话相应的(activity/fragment/service等等)也会被销毁,他们的生命周期不再受开发者控制,而是受到系统控制,所以说尽量不要再应用程序组件中存储任何应用数据和状态,并且各个组件尽量独立不要依赖。 - 常见的构建原则
一个常见的错误是在 Activity 和 Fragment 中编写所有的代码。任何和UI或者操作系统交互无关的代码都尽量不要出现在这些类中,尽量保持这些类的精简会在生命周期由系统造成变化时避免很多麻烦,aac框架就是为了这些问题而生。
通过model驱动ui即通过数据驱动ui(model最好是持久化的model)这样做在系统销毁应用释放资源后我们可以不用在费心的考虑数据丢失的问题,而且在网络不好的情况下由于持久化model的存在应用也可以继续运行。
- 应用开发者所面对的问题
- 如何使用aac框架构建mvvm架构的Android程序
一: 初步配置环境
首先需要引入 Lifecycles, LiveData and ViewModel(该库存在于google的maven仓库中需要在项目gradle中引入google的maven)然后再在module模块下引入相应的库。需要注意的是lifecycle包内会依赖support包也许会和部分项目造成冲突,注意项目内需要使用什么版本。
allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
}
}
需要注意的是目前低版本的support-fragment and appcompat-v7(version<26.0.0)必须使activity和fragment继承LifecycleActivity和LifecycleFragment,在高版本中FragmentActivity 和 AppCompatActivity 都实现了 LifecycleOwner接口不必再继承
//aac
compile "android.arch.lifecycle:runtime:$rootProject.lifecycleRuntime"
compile("android.arch.lifecycle:extensions:${rootProject.lifecycle}")
{
exclude group: 'com.android.support'
}
annotationProcessor "android.arch.lifecycle:compiler:$rootProject.lifecycle"
二:java代码配置部分
model:普通的pojo类即可
public class User {
private int id;
private String name;
// ...getter setter and toString...
}
view:普通的xml文件即可(此处暂时没有结合databinding)
viewmodel:viewmodel就很类似mvc模式中的controller和mvp种的presenter由于我们集成了aac框架所以直接写一个类集成viewmodel就行,它可以帮我们自动管理ui的生命周期
public class UserViewModel extends ViewModel {}
先写一个空方法,具体内部实现后面说。
livedata:轻松实现数据和view(ui)绑定的好工具,livedata可以自动响应生命周期,数据在改变的时候可以通知观察者更新ui,就是因为这点可以轻松实现model和view的绑定
具体用法:
首先创建可变的livedata即mutablelivedata
private MutableLiveData mCurrentName;
public MutableLiveData getCurrentName() {
if (mCurrentName == null) {
mCurrentName = new MutableLiveData();
}
return mCurrentName;
}
其次在activity/fragment中观察livedata。也就是当livedata数据发生变化时,自动回调方法来更新 ui。
Observer nameObserver = new Observer() {
@Override
public void onChanged(@Nullable final String newName) {
mNameTextView.setText(newName);
}
};
getCurrentName().observe(this, nameObserver);
最后更新数据
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String anotherName = "licz";
getCurrentName().setValue(anotherName);
}
});
livedata+viewmodel:为了显示id和name我们需要一个livedata
public class UserViewModel extends ViewModel {
private MutableLiveData user;
public LiveData getUser() {
if (user == null)
user = new MutableLiveData<>();
return user;
}
public void setUsername(String username) {
user.setValue(new User(1, username));
}
}
view+viewmodel:livedata它可以方便的被观察。viewmodel,可以自动响应生命周期。这两个东西的结合可以帮我们方便的实现数据绑定。下面我们来说说viewmodel怎么和view绑定,然后自动响应activity/fragment的生命周期
private UserViewModel userViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
initData();
}
private void initData() {
userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);
userViewModel.getUser().observe(this, new Observer() {
@Override
public void onChanged(@Nullable User user) {
updateView(user);
}
});
userViewModel.setUsername("licz");
}
private void updateView(User user) {
tvId.setText(user.getId() + "");
tvName.setText(user.getName());
}
通常来说,一个view只和一个viewmodel 绑定。所以我们需要一个activity级别的单例viewmodel,我们就是通过ViewModelProviders.of(this).get(UserViewModel.class) 来简单的实现。然后通过userViewModel取得LiveData并添加监听(绑定操作)。
三:aac框架实现总结
这样的一个框架的存在帮助我们解决了很多痛点问题,比如说以后就不需要为同一份数据n处都修改view的问题头疼了,再比如数据请求可以更为纯粹不需要考虑ui是怎么渲染和实现的。再做完数据请求或者完成数据更新以后,livedata会自动通知ui根据数据处理界面。
在aac框架的基础上结合databinding后我们可以完全不需要自己手动处理ui,而viewmodel和model通过livedata作为桥梁相连接,view和viewmodel双向绑定,由于viewmodel自动响应生命周期我们也可以很轻松处理view生命周期相关的ui需求。
最后aac框架不是实现mvvm的必要途径只是他可以为我们解决很多实现mvvm过程中的痛点