Jetpack系列-ViewModel的使用及原理浅析

本文介绍了Android Jetpack中的ViewModel,它可承载业务逻辑、保存屏幕状态,在配置变更后持久保留数据。阐述了其优势,如持久保持界面状态、限定异步工作作用域等,还介绍了使用方法,包括基本用法、SavedStateHandle等,并对其获取过程、生命周期管理等原理进行了分析。

作者:碎星

简介

ViewModel在架构中用于承载业务逻辑和作为容器保存屏幕状态,它可以缓存界面的状态,并且能在配置变更后持久保留相应的界面状态。

在jetpack套件中,ViewModel随lifecycle一起提供。

优势

简介

ViewModel在架构中用于承载业务逻辑和作为容器保存屏幕状态,它可以缓存界面的状态,并且能在配置变更后持久保留相应的界面状态。

在jetpack套件中,ViewModel随lifecycle一起提供。

优势

  • 可以持久的保持界面状态:一是界面因配置变更导致的重建,不会销毁内存中的;二是可以借助SavedStateHandle在进程销毁-重建过程中恢复数据。
  • ViewModel具有作用域(如:Activity、Fragment等),ViewModel中的异步工作将被限定在这个Lifecycle上执行。
  • ViewModel可用用来承载之前处于界面层的部分业务逻辑:将数据层传递的数据处理成界面状态。
  • 可以作为桥梁在Activity与Fragment、Fragment与Fragment之间共享数据。

使用

定义

// 直接继承ViewModel
class DemoViewModel : ViewModel() {
    private val api = MyService()

    // 通常配合LiveData、StateFlow这些可感知对象为界面提供状态。
    private val _uiState = MutableLiveData("")
    val uiState: LiveData<String> = _uiState
    // 使用SharedFlow为界面提供事件回调
    private val _uiEvent = MutableSharedFlow<DemoEvent>()
    val uiEvent = _uiEvent.asSharedFlow()

    fun reqData(param: String) {
        // ViewModel能自动处理协程scope的生命周期
        viewModelScope.launch(Dispatchers.IO) {
            _uiEvent.emit(DemoEvent.Loading)
           try {
                val data = api.reqData(param)
                _uiState.postValue("rsp: $data")
            } finally {
               _uiEvent.emit(DemoEvent.Completed)
            }
        }
    }
}

基本用法

在androidx的Activity中使用:

class ViewModelDemoActivity : AppCompatActivity() {
    private lateinit var viewModel: DemoViewModel
    // viewmodel的ktx扩展库中提供了委托方式获取viewmodel实例
//  private val viewModel: DemoViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_model_demo)
        
        // ViewModelProvider是获取viewmodel的基础工具,它需要一个ViewModelStoreOwner实例
        // 这个实例就是用来存储和管理viewmodel的,androidx的ComponentActivity
        // 实现了这个接口,因此可以直接使用AppCompatActivity来初始化ViewModelProvider。
        viewModel = ViewModelProvider(this).get(DemoViewModel::class.java)

        // 监听界面状态以及事件,并做出响应
        viewModel.uiState.observe(this) {
            Log.d(TAG, "received response: $it")
        }
        viewModel.uiEvent
            .onEach { showLoading(it == DemoEvent.Loading) }
            .launchIn(lifecycleScope)
        viewModel.reqData(param)
    }
}

在androidx的Fragment中使用:

class DemoFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // jetpack fragment也实现了ViewModelStoreOwner接口,因此也可以用于获取和管理viewmodel
        val selfViewModel = ViewModelProvider(this).get(FragmentViewModel::class.java)
    }
    
    override fun onAttach(context: Context) {
        super.onAttach(context)
        // 可以获取到Activity或者其它Fragment的ViewModel,只需要在构造ViewModelProvider
        // 时传递了对应的ViewModelStoreOwner。这个实例和DemoActivity
        // 中获取到的是同一个实例,因此你可以通过这个实例实现和Activity的通信。
        val parentViewModel = ViewModelProvider(requireActivity())
            .get(DemoViewModel::class.java)
    }
}

SavedStateHandle

SavedStateHandle主要用于在进程销毁-重建过程中恢复数据,它可以将数据持久化到存储中,并在重建后恢复数据。

class DemoViewModel(val savedState: SavedStateHandle) : ViewModel() {
    // 可以使用getLiveData将要获取的数据转为LiveData
    private val _savedData = savedState.getLiveData<String>(DATA_KEY)
    val savedData: LiveData<String> = _savedData  

    fun saveData(data: String) {
        savedState[DATA_KEY] = data
    }

    fun readData(): String? {
        // 也可以直接获取
        return savedState[DATA_KEY]
    }

    companion object {
        private const val DATA_KEY = "data"
    }
}

AndroidViewModel

有时候ViewModel中可能会需要使用到Android Context(获取文本、颜色等资源),此时可以使用AndroidViewModel,它提供了一个getApplication()方法,可以很方便的获取上下文实例。使用方式如下:

class DemoViewModel(application: Application) 
    : AndroidViewModel(application) { 
    fun getString() = application.getString(R.string.hint_txt)
}

带参数的ViewModel

前面几个小节我们都假定了使用androidx的组件作为ViewModelStoreOwner来构造ViewModelProvider。这些androidx的组件会帮助我们自动提供ViewModel所依赖的SavedStateHandleApplication

然而,当我们使用自定义ViewModelStoreOwner时,或者想向ViewModel传递其它类型的参数时,就需要自定义ViewModeProvider.Factory了。

假如我们有如下ViewModel,它需要接收一个Repository作为参数:

class MyViewModel(
    private val myRepository: MyRepository
) : ViewModel() { }

为了实例化MyViewModel,我们需要再定义一个Factory,然后在create方法中获取依赖对象,构造ViewModel实例:

val factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(
        m
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值