内容简介:Presenter作为中间人,负责在View和数据Model之间处理业务逻辑。其实这里隐藏了Presenter带来的问题,Presenter的作用不单纯他需要一边与View打交道,一边与数据打交道。只能剥离一些Activity/Fragment的代码。下面已Google architecture sample里MVP的代码作为例子:这是一个加载一个todo list的界面例子
MVP vs MVVM
MVP
MVP代表Model–view–presenter,用图来表示MVP的思想如下:
图片来之wikipedia
Presenter作为中间人,负责在View和数据Model之间处理业务逻辑。其实这里隐藏了Presenter带来的问题,Presenter的作用不单纯他需要一边与View打交道,一边与数据打交道。只能剥离一些Activity/Fragment的代码。
下面已Google architecture sample里MVP的代码作为例子:
这是一个加载一个todo list的界面例子
MVP会先定义接口作为协议
interface TasksContract {
interface View : BaseView<Presenter> {
var isActive: Boolean
fun setLoadingIndicator(active: Boolean)
fun showTasks(tasks: List<Task>)
fun showNoTasks()
// 省略
}
interface Presenter : BasePresenter {
fun loadTasks(forceUpdate: Boolean)
fun result(requestCode: Int, resultCode: Int)
// 省略
}
}
TasksPresenter代码:
class TasksPresenter(val tasksRepository: TasksRepository, val tasksView: TasksContract.View)
: TasksContract.Presenter {
private fun loadTasks(forceUpdate: Boolean, showLoadingUI: Boolean) {
// 这里需要知道view的状态来判断是否需要显示loading,showLoading由view传递过来。这里已经开始让逻辑不单一了
if (showLoadingUI) {
tasksView.setLoadingIndicator(true)
}
// 省略部分代码...
// 加载数据
tasksRepository.getTasks(object : TasksDataSource.LoadTasksCallback {
override fun onTasksLoaded(tasks: List<Task>) {
val tasksToShow = ArrayList<Task>()
// 当View不是active的时候直接返回,这里有要考虑View被销毁,回调还在的问题,代码持续ugly
if (!tasksView.isActive) {
return
}
if (showLoadingUI) {
tasksView.setLoadingIndicator(false)
}
processTasks(tasksToShow)
}
override fun onDataNotAvailable() {
// The view may not be able to handle UI updates anymore
if (!tasksView.isActive) {
return
}
tasksView.showLoadingTasksError()
}
})
}
}
承担View职责的是一个Fragment
class TasksFragment : Fragment(), TasksContract.View {
internal var itemListener: TaskItemListener = object : TaskItemListener {
override fun onTaskClick(clickedTask: Task) {
presenter.openTaskDetails(clickedTask)
}
override fun onCompleteTaskClick(completedTask: Task) {
presenter.completeTask(completedTask)
}
override fun onActivateTaskClick(activatedTask: Task) {
presenter.activateTask(activatedTask)
}
}
// 省略代码...
setOnRefreshListener { presenter.loadTasks(false) }
}
可以从上述代码了解到,MVP模式面临的问题是
- View和Presenter之间需要相互持有
- Presenter职责不单一,作为中间人同时要管理view和数据,让代码看起来丑陋。而我们期望Presenter的作用应该是只对数据进行逻辑操作而不需要关系View的状态。
MVVM
图片来之wikipedia MVVM
MVVM是通过数据绑定,让原来的View和Presenter的职责更单一的改进。Model提供数据,ViewModel来做业务逻辑处理和管理状态,View只更具ViewModel的数据自己变化显示。
这样的想法很好,如何在Android里实现呢,答案是ViewModel和LiveData
ViewModel + LiveData实现MVVM
Android里去年引入的ViewModel类,被设计用来保存和View显示逻辑相关的数据,它是忽略configuration change的一个类,就算因为configuration change数据依然会被保留。
LiveData可以简单理解为一个放数据的容器,并且知晓UI的生命周期,它是一个数据的观察者它可以知晓数据的变化,所以叫它LiveData活数据嘛。
同样是前面MVP展示的逻辑,一个todo list应用需要加todo列表
public class TasksViewModel extends AndroidViewModel {
// 数据列表
public final ObservableList<Task> items = new ObservableArrayList<>();
// loading状态
public final ObservableBoolean dataLoading = new ObservableBoolean(false);
// 省略代码...
private void loadTasks(boolean forceUpdate, final boolean showLoadingUI) {
// 只用设置loading的状态,不再需要持有View
if (showLoadingUI) {
dataLoading.set(true);
}
mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() {
@Override
public void onTasksLoaded(List<Task> tasks) {
// 单纯的更新数据,而不再用操心View的状态
List<Task> tasksToShow = new ArrayList<>();
if (showLoadingUI) {
dataLoading.set(false);
}
mIsDataLoadingError.set(false);
items.clear();
items.addAll(tasksToShow);
empty.set(items.isEmpty());
}
@Override
public void onDataNotAvailable() {
mIsDataLoadingError.set(true);
}
});
}
}
View的逻辑,这里用了Android的DataBinding,loading的显示更具ViewModel里的值来判断
<SwipeRefreshLayout
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onRefresh="@{viewmodel}"
app:refreshing="@{viewmodel.dataLoading}"/>
总结,MVVM可以让View的职责更加单一只是根据数据来显示。ViewModel可以单纯的处理数据而不用关心View的状态
ViewModel的其他好处
-
Fragment间传递数据
Fragment之前直接传递数据是如此的痛苦,小数据放bundle put也就忍了,数据太大还只能通过直接持有Fragment的引用传入数据引用。现在有了ViewModel可以很方便的传递数据,因为在同一个Activity里可以获得到同一个ViewModel,数据可以直接拿来用。
这样的好处显而易见,
- Activity不需要了解Fragment直接通信的过程
- Fragment直接也不需要互相了解,只需要从ViewModel里拿数据就好了
- 数据会因为是通过引用传递过去的,其中一个Fragment因为生命周期而影响数据
-
测试方便
ViewModel不再持有具体的Android类,View啊Activity,Fragment啥的,给测试单独ViewModel提供了可能。
-
代替onSaveInstanceState保存状态
Fragment之前保存数据和恢复状态一直很疼,需要自己onSaveInstanceState,并且因为onSaveInstanceState用的bundle并不能放入大量数据,只能简单的保存一些简单的状态。但是现在有ViewModel它不会随着configuration change而变化,数据就静静的呆在那所以Fragment被回收后从新创建也不用担心数据丢失了。
ViewModel的秘密
ViewModel的神奇的一点是它到底如何逃离configuration change导致的生命周期变化的呢?
其实非常简单,是利用了Fragment的
Fragment.setRetainInstance(true) 。
看源码:
ViewModelStores是保存ViewModel的地方
ViewModel是重HolderFragment里取出。
HolderFragment
参考
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 为什么你应该从学习数据可视化和操作开始
- 顶会论文应不应该提交代码?应该,但不能强制
- 单元测试 – 我应该对不应该在函数中传递的数据(无效输入)进行单元测试吗?
- 智能合约事件应该这么用
- 研发职位到底应该怎么设置?
- 我们应该如何给需求排序?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web 2.0界面设计模式
黄玮 / 电子工业出版社 / 2013-9-1 / 59
本书集Web 2.0的发展及特点、Web 2.0界面设计模式基本理论、实际模式实践及代码实现等诸多内容于一身,具有很强的实用性。这些内容不是简单的顺序堆砌,而是以Web 2.0界面设计模式和应用为主线,其中完美地穿插了各种与之相关的Web 2.0设计理念、用户行为模式、用户体验及基于Dojo的实现方式等相关知识,真正做到将Web 2.0界面设计模式所需要的方方面面的知识有机地融为一个整体。实现不需......一起来看看 《Web 2.0界面设计模式》 这本书的介绍吧!