ViewModel+LiveData
Android在前段时间正式发布 Android 架构组件 1.0 稳定版,里面包含Lifecycles,LiveData,ViewModel,Room。这里主要记录一下LiveData和ViewModel配合使用。
LiveData
LiveData 是一款基于观察者模式的可感知生命周期的核心组件。LiveData 为界面代码 (Observer)的监视对象 (Observable),当 LiveData 所持有的数据改变时,它会通知相应的界面代码进行更新。同时,LiveData 持有界面代码 Lifecycle 的引用,这意味着它会在界面代码(LifecycleOwner)的生命周期处于 started 或 resumed 时作出相应更新,而在 LifecycleOwner 被销毁时停止更新。通过 LiveData,开发者可以方便地构建安全性更高、性能更好的高响应度用户界面
使用LiveData的好处
- 保证你的UI 能根据数据状态发生改变
- 不会有内存泄漏
- 不会因为Activity的stop造成crashes
- 不用手动去控制生命周期
- 永远保证数据更新
ViewModel
ViewModel 将视图的数据和逻辑从具有生命周期特性的实体(如 Activity 和 Fragment)中剥离开来。直到关联的 Activity 或 Fragment 完全销毁时,ViewModel 才会随之消失,也就是说,即使在旋转屏幕导致 Fragment 被重新创建等事件中,视图数据依旧会被保留。ViewModels 不仅消除了常见的生命周期问题,而且可以帮助构建更为模块化、更方便测试的用户界面。
ViewModel的作用和好处
- Activity、Fragment存活期间的数据存储;
- 独立或与LiveData配合实现代码解耦;
ViewModel的生命周期
ViewModel的生命周期与Lifecycle同步,当Activity /Fragment超出Lifecycle范围(并不是onDestroy()回调),ViewModel连同其包含的数据一起被销毁了。
Activity退栈后(Fragment则是与Activity解除关系后),ViewModel就解除引用关系,准备被系统回收了。下面这张图片更直观的展示了ViewModel的生命周期:
具体使用
-
引入ViewModel和LiveData
在build.gradle中添加编译lifecycle.extensions module,该module同时包含ViewModel和LiveData:
compile('android.arch.lifecycle:extensions:1.0.0')
- 构造ViewModel
借用官方提供的例子,去获取一个用户的数据,这里直接引入LiveData构造一个ViewModel.
public class UserProfileViewModel extends ViewModel {
...
private User user;
private LiveData<User> user;
public LiveData<User> getUser() {
return user;
}
}
-
获取数据
首先通过ViewModelProviders和Activity / Fragment获取ViewModelProvider;
然后根据ViewModelProvider和自定义ViewModel类获取自定义ViewModel对象;
public class UserProfileFragment extends Fragment {
private static final String UID_KEY = "uid";
private UserProfileViewModel viewModel;
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String userId = getArguments().getString(UID_KEY);
viewModel.getUser().observe(this, user -> {
// update UI
});
}
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.user_profile, container, false);
}
}
进行到这一步,基本实现了每一次User数据更新就会回掉刷新UI界面。而且并不需要去实现Fragment的onStop()方法来停止观察数据。因为LiveData是一个有生命周期意识的组件,它不需要我们去手动的停止,而且会在onDestroy()中自动的remove 掉这个观察事件。
另外也并不需要去管屏幕的旋转,因为当屏幕旋转的时候ViewModel会自动保存下当前Fragment的数据和状态。
经过上面的步骤已经将ViewModel和fragment关联起来了,但是并没有真正的通过ViewModel去获取数据。下面记录一下如何通过Retrofit 去获取数据:
public interface Webservice {
/**
* @GET declares an HTTP GET request
* @Path("user") annotation on the userId parameter marks it as a
* replacement for the {user} placeholder in the @GET path
*/
@GET("/users/{user}")
Call<User> getUser(@Path("user") String userId);
}
新建一个UserRepository
public class UserRepository {
private Webservice webservice;
// ...
public LiveData<User> getUser(int userId) {
// This is not an optimal implementation, we'll fix it below
final MutableLiveData<User> data = new MutableLiveData<>();
webservice.getUser(userId).enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
// error case is left out for brevity
data.setValue(response.body());
}
});
return data;
}
}
就像官方描述的UserRepository并不是必须的,把它抽象出来,这样ViewModel就不知道数据是通过Webservice来获取的,好处就是which means we can swap it for other implementations as necessary.
最后将UserRepository和ViewModel关联起来,修改ViewModel:
public class UserProfileViewModel extends ViewModel {
private LiveData<User> user;
private UserRepository userRepo;
@Inject // UserRepository parameter is provided by Dagger 2
public UserProfileViewModel(UserRepository userRepo) {
this.userRepo = userRepo;
}
public void init(String userId) {
if (this.user != null) {
// ViewModel is created per Fragment so
// we know the userId won't change
return;
}
user = userRepo.getUser(userId);
}
public LiveData<User> getUser() {
return this.user;
}
}
总结
就这样一个完整的ViewModel+LiveData的例子就完成了。
ViewModel+LiveData可以很方便的抽象出数据层和业务层,快速解耦。
通过Demo,以及LiveData、ViewModel同处一个module,可以看出官方非常建议两者搭配使用。再配合以往的Data-Binding,可以快速搭建起一套简易的MVVM业务体系。
最后通过一张图可以大概的了解下Android官方架构:
推荐阅读