美文网首页
Jetpack学习(三) ViewModel

Jetpack学习(三) ViewModel

作者: 飞哥278999401 | 来源:发表于2021-05-29 21:07 被阅读0次

    一、配置

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

    • 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
        }
    }
    
    

    相关文章

      网友评论

          本文标题:Jetpack学习(三) ViewModel

          本文链接:https://www.haomeiwen.com/subject/obzysltx.html