Jetpack学习(三) ViewModel

2021-05-29  本文已影响0人  飞哥278999401

一、配置

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生命周期

ViewModelActivityfragment,同生共死。

image.png

三、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,此方法具有以下优势:

五、将协程与 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
    }
}

上一篇下一篇

猜你喜欢

热点阅读