一、配置
ViewModel
类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel
类让数据可在发生屏幕旋转等配置更改后继续留存。
def lifecycleVersion = '2.2.0'
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
二、ViewModel生命周期
ViewModel
和Activity
、fragment
,同生共死。
三、a、ViewModel例子(ViewModel无参数)
class OneFragment : Fragment() {
val model: UserViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.getUsers().observe(viewLifecycleOwner, Observer<List<User>> { users ->
Log.d("haha", "OneFragment"+Thread.currentThread().name)
Log.d("haha",users.toString())
})
model.loadUsers()
}
}
class UserViewModel : ViewModel() {
private val users: MutableLiveData<List<User>> = MutableLiveData<List<User>>()
fun getUsers(): LiveData<List<User>> {
return users
}
fun loadUsers() {
Thread(Runnable {
//模拟异步获取数据
Log.d("haha", "UserViewModel" + Thread.currentThread().name)
Thread.sleep(5000)
val user1 = User("zhang", "san")
val user2 = User("li", "si")
val list = mutableListOf<User>()
list.add(user1)
list.add(user2)
Log.d("haha", "UserViewModel" + "loadUsers finish")
users.postValue(list)
}).start()
}
}
如果重新创建了该 Activity,它接收的 MyViewModel
实例与第一个 Activity 创建的实例相同。当所有者 Activity 完成时,框架会调用 ViewModel
对象的 onCleared()
方法,以便它可以清理资源。
b、ViewModel例子(ViewModel有参数)
比如,我们要远程获取一个User,这个时候ViewModel需要一个UserRepository,这个时候我们需要自定义获取ViewModel,官方为我们提供了一个ViewModelProvider.Factory工厂类帮我们创造ViewModel,我们实现它即可。如下:
class UserViewModel(private val userRepository: UserRepository) : ViewModel() {
private val users: MutableLiveData<List<User>> = MutableLiveData<List<User>>()
fun getUsers(): MutableLiveData<List<User>> {
return users
}
fun loadUsers() {
viewModelScope.launch {
val list = userRepository.getUsers()
users.value = list
}
}
}
class UserViewModelFactory(private val userRepository: UserRepository) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return UserViewModel(userRepository) as T
}
}
class UserRepository {
suspend fun getUsers(): List<User> {
//模拟异步获得网络数据
delay(5000)
val list = mutableListOf<User>()
list.add(User("zhang", "san"))
list.add(User("haha", "haha"))
return list
}
}
object InjectorUtils {
fun providerUserViewModelFactory(): UserViewModelFactory {
val userRepository = UserRepository()
return UserViewModelFactory(userRepository)
}
}
使用
class OneFragment : Fragment() {
val model: UserViewModel by viewModels() {
InjectorUtils.providerUserViewModelFactory()
}
}
注意:ViewModel
绝不能引用视图、Lifecycle
或可能存储对 Activity 上下文的引用的任何类。
四、在同一个activity中多个Fragment 之间共享数据
Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的现象。想象一下拆分视图 (master-detail) Fragment 的常见情况,假设您有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。这种情况不太容易处理,因为这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。
class SharedViewModel : ViewModel() {
val sharedUser = MutableLiveData<User>()
fun select(user: User) {
sharedUser.value = user
}
}
class OneFragment : Fragment() {
val sharedViewModel: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sharedViewModel.setSharedUser(User("hahah", "hahaha"))
}
}
class TwoFragment : Fragment() {
val sharedViewModel: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d("haha", sharedViewModel.sharedUser.value!!.firstName)
sharedViewModel.sharedUser.observe(viewLifecycleOwner, Observer<User> {
})
}
}
两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各自获取 ViewModelProvider
时,它们会收到相同的 SharedViewModel
实例(其范围限定为该 Activity)。
都去使用activityViewModels,此方法具有以下优势:
- Activity 不需要执行任何操作,也不需要对此通信有任何了解。
- 除了
SharedViewModel
约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。 - 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。
五、将协程与 ViewModel 一起使用
每个 ViewModel
定义了 ViewModelScope
。如果 ViewModel
已清除,则在此范围内启动的协程都会自动取消。如果您具有仅在 ViewModel
处于活动状态时才需要完成的工作,此时协程非常有用。例如,如果要为布局计算某些数据,则应将工作范围限定至 ViewModel
,以便在 ViewModel
清除后,系统会自动取消工作以避免消耗资源。
class UserViewModel(private val userRepository: UserRepository) : ViewModel() {
private val users: MutableLiveData<List<User>> = MutableLiveData<List<User>>()
fun getUsers(): MutableLiveData<List<User>> {
return users
}
fun loadUsers() {
viewModelScope.launch {
val list = userRepository.getUsers()
users.value = list
}
}
}
六、协程+okhttp+retrofit一起使用
okhttp+retrofit目前支持suspend函数,可以直接使用,以前我们使用retrofit时,会设置返回值为Call去调用enqueue(异步回调)或者调用execute(同步调用)。协程可以直接网络返回结果即可,这里直接主线程调用即可,协程不会卡住主线程,不需要使用withContext切换线程。例如:
//定义
@GET("users")
suspend fun getUsers(
@Query("page") page: Int,
@Query("per_page") perPage: Int
): UserResponse
//使用
class UserRepository(userService:UserService) {
suspend fun getUsers(): List<User> {
val response = userService.getUsers( page, 10)
return response.list
}
}
网友评论