因为不太喜欢mvp框架的大量冗余代码,所以呢,很长时间用着被大家诟病的mvc结构。去年呢,kotlin大量的流行了,趁着换kotlin的机会,也换上了mvvm.
MVVM是增强关注点分离的体系结构模式之一,它允许将用户界面逻辑从业务(或者后端)逻辑中分离开来,他的目标(和MVC等其他目标)是为了实现”保持UI代码简单化,不涉及更多的业务逻辑,以便于开发者更好的控制和管理”。
MVVM主要有以下几个层次:
1、Model层
Model层表示用户程序的数据和业务逻辑,这一层的的推荐的实现策略之一就是观测数据的变化并传递出去(供谁使用),使其从ViewModel或者其他观察者/消费者中完全解耦
2、ViewModel层
ViewModel是和Model(数据层)进行交互,并且ViewMode可以被View观察.ViewModel可以选择性地为视图提供钩子以将事件传递给模型.该层的一个重要实现策略是将Model与View分离,即ViewModel不应该意识到与谁交互的视图.
3、View层
此模式中的视图角色是观察(或订阅)ViewMode,观察数据b变化,以便于获取数据去更新UI元素.
1、LiveData
如上所述,LiveData是新引入的组件架构之一,LiveData是一个可以被观察的数据持有者.这也就意味着应用中的组件能够观察LiveData对象的更改,而无需在它们之间创建明确的和严格的依赖关系。这将完全分离LiveData对象使用者和LiveData对象生产者。
除此之外,LiveData还有一个很大的好处,LiveData遵守应用程序组件(活动,片段,服务)的生命周期状态,并进组件的生命周期管理,确保LiveData对象的内存泄漏.
2、ViewModel
ViewModel也是新引入的体系架构组件之一.架构组件提供了一个名为ViewModel的新类,它负责为UI / View准备数据.
ViewModel为您的MVVM模式中的ViewModel提供了一个很好的基类,因为ViewModel(及其子类AndroidViewModel)的扩展类会在配置更改期间自动保留其数据.这意味着,在配
置更改后,此ViewModel所有数据可立即用于下一个活动(activity)或片段(fragment)实例.
viewmodel生命周期随view组件的生命周期而销毁,不会内存泄露。ViewModel类是被设计用来以可感知生命周期的方式存储和管理 UI 相关数据,ViewModel中数据会一直存活即使 activity configuration发生变化,比如横竖屏切换的时候。
数据库结构往往是不能直接跟界面控件一一对应上的,所以,需要再定义一个数据对象专门对应view上的控件。而ViewModel的职责就是把model对象封装成可以显示和接受输入的界面数据对象。
解耦view和model之间,viewmodel作为中间协调,分离出数据操作的职责给 ViewModel,网络请求和数据库操作都放到viewmodel进行
简单的说,ViewModel就是View与Model的连接器,View与Model通过ViewModel实现双向绑定。数据库结构往往是不能直接跟界面控件一一对应上的,所以,需要再定义一个数据对象专门对应view上的控件。而ViewModel的职责就是把model对象封装成可以显示和接受输入的界面数据对象。
3、viewmodel作用;
1)数据持久化
ViewModel 生命周期是贯穿整个 activity 生命周期,包括 Activity 因旋转造成的重创建,直到 Activity 真正意义上销毁后才会结束。既然如此,用来存放数据再好不过了。
public class MyViewModel extends ViewModel {
private MutableLiveData> users;
public LiveData> getUsers() {
if (users == null) {
users = new MutableLiveData>();
loadUsers();
}
return users;
}
private void loadUsers() {
// do async operation to fetch users
}
}
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
2)异步回调问题
通常我们 app 需要频繁异步请求数据,比如调接口请求服务器数据。当然这些请求的回调都是相当耗时的,之前我们在 Activity 或 fragment里接收这些回调。所以不得不考虑潜在的内存泄漏情况,比如 Activity 被销毁后接口请求才返回。处理这些问题,会给我们增添好多复杂的工作。但现在我们利用 ViewModel 处理数据回调,可以完美的解决此痛点。
3、分担 UI controller负担
从最早的 MVC 到目前流行的 MVP、MVVM,目的无非是 明确职责,分离 UI controller 负担。
UI controller 比如 Activity 、Fragment 是设计用来渲染展示数据、响应用户行为、处理系统的某些交互。如果再要求他去负责加载网络或数据库数据,会让其显得臃肿和难以管理。所以为了简洁、清爽、丝滑,我们可以分离出数据操作的职责给 ViewModel。
4、Fragments 间共享数据
比如在一个 Activity 里有多个fragment,这fragment 之间需要做某些交互。我之前的做法是接口回调,需要统一在 Activity 里管理,并且不可避免的 fragment 之间还得互相持有对方的引用。仔细想想就知道这是很翔的一件事,耦合度高不说,还需要大量的容错判断(比如对方的 fragment 是否还活着)。那么用 ViewModel 是怎么样的呢(官网例子):(activity 与其内部的 fragment 可以共用一个ViewModel)
public class SharedViewModel extends ViewModel {
private final MutableLiveData- selected = new MutableLiveData
- ();
public void select(Item item) {
selected.setValue(item);
}
public LiveData
- getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
仔细体会下这样的好处会发现:
1、Activity 不需要做任何事,甚至不知道这次交互,完美解耦。
2、Fragment 只需要 与ViewModel交互,不需要知道对方 Fragment 的状态甚至是否存在,更不需要持有其引用。所有当对方 Fragment 销毁时,不影响本身任何工作。
3、Fragment 生命周期互不影响,甚至 fragment 替换成其他的 也不影响这个系统的运作。
官网用一个大大的红色感叹号表明:
Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the
activity context.由于 ViewModel 生命周期可能长与 activity 生命周期,所以为了避免内存泄漏 Google 禁止在 ViewModel 中持有 Context 或 activity 或 view 的引用。
可以发现 AndroidViewModel 类,内部维护了一个 ApplicationContext ,实在需要可以用这个
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
/**
* Return the application.
*/
@NonNull
public T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}