Android(安卓)MVVM ViewModel
不要忘记View Model!(翻译)
1、背景
最近,我看了许多在android方面的文章,比如androiddev and Android Weekly.这些文章是非常精彩的。坦白的说,一些年以前我从Window Phone开发转入到Android开发,我感觉到构造一个稳定可靠的app是非常困难的。Google的一些example也违反的最佳原则[1]。因此我想从我这些年.net的平台开发经验中写自己的MVVM版本(或者MVPVM)。看了Hannes Dorfmann’s发布的MVP框架在他的Mosby 中,我知道我不是唯一一个寻求稳定模式的开发者(他花了3年时间),Android SDK并不是你唯一学习架构途径。比如Fragment中的一些问题,受到Square Inc的反对 。
无论如何,这都应该朝着一个良好的UI框架发展,这是常识。架构。架构[2]对我们是有帮就像Dorfmann’s 和 Artem Zin’s发表的模型。然而,在Android中很少听到View Model-这真的不应该为不知道它而找什么借口。View Model 这一概念是非常好的在任何的MVC/MVP的模型中,在这篇文章中,我将解释如何把你的模型分成两个甚至三个的模型。
[1]比如网络框架,View,Model和Presenter的代码全部在一个文件中,并且在内部类中的任务都依赖于activity,在解析xml文件的时候也有一些错误。因此我们是否需要第三方的库来简化我们的xml解析和http通信。
[2]除了一些框架库外,还有一些小的库或者微型框架比如:Dagger, Flow, Mortar, Parceler, Icepick 和 RxAndroid都为MVP架构铺平了道路
2、不同的模型类型
VIEW MODEL
在这篇文章中我将描述一个松散的模型去映射具体的接口。Martin Fowler是Presentation的模型。这不同于商业模式它在一个特定的上下文中过滤、合并或转换的数据。之后,你可以有多个View Models给一个商业模式或者多个商业模式对就一个View Model。微软MVVM模式是紧耦合的业务模型,通过数据绑定。这不是我定义和使用的要求,如果这是你的项目的要求,它也肯定可以实现。
THE BUSINESS MODEL(业务或者商务模型)
我为什么命名了Business Model,它实际上是一个商业模式。想想在网上商店系统中的用户、产品、供应商或订单。在Android中代码中它包含了强类型的对象和与这些工作的商业模式。
THE TRANSPORT MODEL(传输模型)
我将介绍第三种模型,我称之为The Transport Model你不会总是需要它,即使你这样做,你不会总是把它作为一个单独的类。THE TRANSPORT MODEL是代表在运输中的商业模式的模型。如果你有一个REST service,它将代表JSON数据的类。单独作为一个独立的类是非常有用的,因为它允许我单元测试分析容易,以及允许切换传输协议尽可能小的冲突。这种模式工作非常好比如Gson或Jackson库。
3、Example
我现在展示一个电影数据库的app(以前听过吗)。这app是一个集合的json数据,我们需要请求数据并且显示在列表中,下面的是json数据:
{ "v" : 1, "data" : [{ "released" : "2011-08-04", "title" : "Hell on Wheels", "category" : "TV series", "id" : "tt1699748", "stars" : "Anson Mount, Colm Meaney", "image" : [ "http://ia.media-imdb.com/images/M/MV5BMTQ5NTE5NTYzMF5BMl5BanBnXkFtZTgwOTc4OTY0MzE@._V1_.jpg", 1297, 1404 ] }, { "released" : "2014-04-02", "title" : "Hello Ladies: The Movie", "category" : "TV movie", "id" : "tt3762944", "stars" : "Stephen Merchant, Christine Woods", "image" : [ "http://ia.media-imdb.com/images/M/MV5BMTQ5MjYxMjkwOV5BMl5BanBnXkFtZTgwODE3MjY0MzE@._V1_.jpg", 1012, 1500 ] } ]}
这个json数据我想说明一个典型的问题:数据中包含一些不必要的对象(版本号),你不想要这些不必要的数据在你的app商业模型中,但希望日期、图像和星星列表作为强类型数据对象,所以创建了一组与业务模型分离的传输模型:
public class MovieJsonModel { public String released; public String title; public String category; public String id; public String stars; public ArrayList
上面的类只需要一行代码用Gson去解析:
MovieListJsonModel transportModel = new GsonBuilder().fromJson(jsonString, MovieListJsonModel.class);
商业模式看起来有所不同:
public class MovieModel { public String title; public String id; public Uri imageUri; public Date releaseDate; public ArrayList stars; // Just illustrating that the model can and should contain business logic, // not only data. public boolean isReleased() { return releaseDate != null && releaseDate.before(new Date()); }}
(创建一个列表只实例化了ArrayList)
因此,在这里,看到传输模型中的许多字符串字段都得到了一个强类型的对应字段。日期,图像URI星星列表实际上是一个ArrayList。我没有包含在这里的转换代码,但我会建议把它放在传输模型类。这样,您将不会引入从业务模型到传输协议的依赖关系。
最后一个视图模型如何看的例子:
public class MovieViewModel { public String title; public String releaseDate; public Bitmap image; public int backgroundColor; public boolean isSelected;}
这样日期仍然是以字符串的方式表示,想要控件一个java对象,我们创建了一个View Model,仍然可以进行单元测试。此外,们如果要加载一个图片,以前使用的都是一个网址。这当然要求我们实际加载的是图片。我不太在确定在视图模型或业务模型这些代码应该写在哪里。
最后,可能有专门的字段给view,比如背景颜色,状态选择。也有可能从其他Business Models中获取数据,但我这个example中没有包含。
保持状态
View Model另一个好处是保存你的状态在模型是。比如你的系统进程被杀死,或者手机旋转的时候都要这样做。Parceler框架使这个变得简单的,但我们仍然需要一些调整,下面是没有打包的图片对象。
@Parcelpublic class MovieViewModel { public String title; public String releaseDate; @Transient public Bitmap image; public int backgroundColor; public boolean isSelected; // Reference properties: public String imageUrl; // A service client for downloading images asynchronously w/Rx private ImageService mImageService; }
现在包含一个图片的URL字符串。使用RxAndroid加载图片并回调代码如下:
public Observable loadDataAsync() { if (image != null) return Observable.from(new Void[0]); else { Observable imageObs = mImageService.loadImageAsync(movieModel.imageUri); Observable doneObs = imageObs .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .map(new Func1() { @Override public Void call(Bitmap bitmap) { image = bitmap; return null; } }); return doneObs; }}
View,Fragment,或者Activity在onResume()方法中加载图片,在onCreate()方法中确保进View Model已经初始化:
public class MovieActivityprivate MovieViewModel mViewModel;@Overridepublic void onCreate() { super.onCreate(Bundle savedInstanceState); if (savedInstanceState != null) { mViewModel = (MovieViewModel) savedInstanceState.getParcelable("ViewModel"); }}@Overridepublic void onSaveInstanceState(Bundle outState) { outState.putParcelable("ViewModel", mViewModel); super.onSaveInstanceState(outState);}@Overridepublic void onResume() { super.onResume(); if (mViewModel != null) { loadAndBindViewModel(); } else { // Load the models asynchronously from the REST Service, // then call loadAndBindViewModel() } } private void loadAndBindViewModel() { mViewModel.loadDataAsync().subscribe(new Action1() { @Override public void call(Void dummy) { modelToUi(); } }); } private void modelToUi() { // Map all View Model properties to actual view controls }
一些情况下,可能需要Business Model 更好,比如你做本地缓存数据,这时候可能不想保存它到绑定的Activity或者Fragment中,而是保存到Shared Preferences,SD卡或者数据库中。在这篇文章是我不会详细的介绍。
4、总结
MVC/MVP内部的模型,我都已经涉及到了,但坚持View Model 模式,就会单独的表示传输,保持,业务和表示层。创建一个单独的视图模型类将确保不会将相关的东西放在业务层中,或将相关字段插入到业务或视图层中。坚持ViewModel类再绑定Parceler能够保留视图的状态即使设备旋转或进程被杀死。
推荐观看原地址
http://tech.vg.no/2015/04/06/dont-forget-the-view-model/
更多相关文章
- Android直接连接数据库服务器
- Android(安卓)拍照或从相册取图片并裁剪
- Android中WebView拦截js请求
- Android中AsyncTask异步加载
- Android中ListView 控件与 Adapter 适配器如何使用?
- Android应用架构之MVP模式
- [置顶] 【Android(安卓)Training】置顶索引
- Android(安卓)Input Framework(二)---EventHub
- Android的swift语言-Kotlin(一)