Jetpack是Android提供的一系列的工具和组件的集合(套件),它主要目的是为了让开发人员能专注于核心业务,为公司做出更大的商业贡献(省钱)。看看google官方是怎么表述的吧
Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法、减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者可将精力集中于真正重要的编码工作。
ViewModel 主要是用来保存应用UI数据的类。
比如说屏幕旋转事件中,重新创建Activity实例时,可根据ViewModel中的数据进行恢复,使得用户看上去没什么变化。实际上在技术层面,页面上是一个全新的对象。
最初Android中的数据都是直接放在Activity或者是Fragment中的,随着项目体量增加会出现各种各样的问题。最明显的一个就是不易于维护了,各种代码杂糅在一起,很混乱。Google开发团队也意识到这个问题,开始遵循单一责任原则去设计了ViewModel,当然还有一系列的其他组件,也就是整个Jetpack体系。
ViewModel遵循单一责任原则,按照规范,每一个类中只包含一个责任。
注意
1. ViewModel只负责存储UI数据,因此不要将Context传入到ViewModel中来。
也就是View、Fragement、Activity都不可传入到ViewModel中来。否则可能导致内存泄露,进而可能导致OOM问题,更重要的是破坏了ViewModel的规范。
2. ViewModel不应取代onSaveInstanceState的使用。
ViewModel: 在进程被关闭时会被销毁,主要用于存储大量数据的。
onSaveInstanceState: 存储少量数据且必须是可序列化的类型。进程被关闭时,数据不受影响。
3. ViewModel默认构造器是无参的。通过ViewModelProvider.Factory 可以创建ViewModel的自定义构造函数。
Activity负责显示UI,接收用户互动,处理UI数据交给Presenter来做。
ViewModel可以通过Repository来存储和加载应用数据的API。
ViewModel之于反应式UI
override fun onCreate(savedInstanceState: Bundle?){
val binding = ActivityMainBinding.inflate(layoutInflater)
binding.viewmodel = userVeiwModel
binding.setLifecycleOwner(this)
setContentView(binding.root)
}
看看对应的xml文件
<layout>
<data>
<variable name="viewmodel"
type="android.eg.com.UserProfileViewModel" />
</data>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewmodel.user.name}" />
</layout>
ViewMOdel结合LiveData、Databinding构建反应式UI,在layout中绑定数据,从而实现数据更新,界面自动更新的效果。这个应该是google开发团队也借鉴了其他语言中一些流行库的吧,与时俱进。
实现 ViewModel
举个例子,通过ViewModel来获取用户列表。
class MyViewModel : ViewModel() {
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData<List<User>>.also {
loadUsers()
}
}
fun getUsers(): LiveData<List<User>> {
return users
}
fun loadUsers(): MutableLiveData<List<User>> {
// 后台加载用户数据操作
}
}
然后在Activity中访问该用户列表
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?){
val model: MyViewModel by viewModels()
model.getUsers().observe(this, Observer<List<User>>{ users ->
// update UI
})
}
}
如果重新创建了该 Activity,它接收的 MyViewModel
实例与第一个 Activity 创建的实例相同。当所有者 Activity 完成时,框架会调用 ViewModel 对象的 onCleared() 方法,以便它可以清理资源。
如果 ViewModel 需要
上下文(例如,为了查找系统服务),它可以继承AndroidViewModel 类并设置用于接收
Application
的构造函数,因为
Application
类会扩展
Application
。
Context
ViewModel的生命周期
先来看一张图
从图中可以看出,ViewModel的生命周期从第一次调用onCreate开始到结束而结束。
在 Fragment 之间共享数据
Fragment之间共享数据是个非常常见的场景。现在有了ViewModel使得这种共享数据通信的实现方式变得更加的简单。
//不同Fragment共享ViewModel数据
class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>()
fun select(item: Item) {
selected.value = item
}
}
class ListFragment : Fragment() {
private lateinit var itemSelector: Selector
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
}
class DetailFragment : Fragment() {
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
// Update the UI
})
}
}
对应的Java代码如下
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class ListFragment extends Fragment {
private SharedViewModel model;
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), item -> {
// Update the UI.
});
}
}
不同的Fragment通过new获取的SharedViewModel实例是同一个对象(其范围限定为该 Activity),逻辑看起来也更加的简洁
此方法具有以下优势:
- Activity 不需要执行任何操作,也不需要对此通信有任何了解。
- 除了
SharedViewModel
约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。- 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。
ViewModel 与 Room 和 LiveData 一起使用可替换加载器
在使用加载器的一种常见方法中,应用可能会使用 CursorLoader
观察数据库的内容。当数据库中的值发生更改时,加载器会自动触发数据的重新加载并更新界面。
看流程图如下:
现在使用ViewModel和Room、LiveData来解决问题。
ViewModel 确保数据在设备配置更改后仍然存在。
Room 在数据库发生更改时通知 LiveData
LiveData 进而使用修订后的数据更新界面。
如图所示:
参考资料https://developer.android.google.cn/topic/libraries/architecture/viewmodel#java