D21/ 怎麽结合 ViewModel 和 Compose? - ViewModel

今天大概会聊到的范围

  • viewModel in Compose

今天的主题很单纯:如果专案中有使用到 Compose 又有用到 Android Architecture Component 的 ViewModel 与 LiveData 的话,要怎麽让资料在 Compose 内外传递呢?

首先,先建立今天的主角 ViewModel

class FooViewModel : ViewModel() {
    
    val buzzLiveData: LiveData<List<String>>
        get() = _buzzLiveData
    
    private val _buzzLiveData = MutableLiveData<List<String>>(emptyList())
    
    fun addData(param: String) {
        _buzzLiveData.value = _buzzLiveData.value?.let { it + listOf(param) }
    }
}

ViewModel 可以在 Activity 层建立,方法就和一般的 ViewModel 一样:

class BarActivity : ComponentActivity() {
    
    private val vm: FooViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            FooComposable(vm) // <-- 
        }
    }
}


@Composable
fun FooComposable(vm: FooViewModel) { ... }

在 Activity 建立 ViewModel 後,可以将其当成一般的参数传入 Composable。

@Composable
fun FooComposable(model: FooViewModel) {
    
    val data by model.buzzLiveData.observeAsState(initial = emptyList())  // <--- 1
    
    Column {
        LazyColumn {
            items(data) {        // <--- 2
                Text(it)
            }
        }
        
        Button({ model.addData("one") }) {    // <--- 3
            Text("add data")
        }
    }
}

observeAsState 需要 implementation "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07"

  1. 取得 viewModel 中的 LiveData 之後,可以透过 observeAsState 来将 LiveData 转换成 State
  2. 在 composable 中,就可以使用这个 State 当成资料运作
  3. 一般对 ViewModel 的 function 呼叫也可以正常运作

注意!呼叫 ViewModel function 的部分若是放在 composable function 中,会变成 side-effect 。若真的要执行需要特别处理 ( 上一篇的主题 )
Button 的 onClick callback 不是 composable function

@Composable
fun FooComposable(model: FooViewModel = viewModel()) { ... }

若不一定要外部的使用者提供 ViewModel,也可以透过 viewModel() 来现场建立一个 viewModel。

observeAsState Under the hood

observeAsState 到底怎麽把 LiveData 转成 State 的呢?我们可以来研究一下 observeAsState 的实作:

@Composable
fun <R, T : R> LiveData<T>.observeAsState(initial: R): State<R> {
    val lifecycleOwner = LocalLifecycleOwner.current      // < --- 1
    val state = remember { mutableStateOf(initial) }      // < --- 2
    DisposableEffect(this, lifecycleOwner) {                           // < --- 5
        val observer = Observer<T> { state.value = it }   // < --- 3
        observe(lifecycleOwner, observer)                
        onDispose { removeObserver(observer) }                         // < --- 6
    }
    return state                                          // < --- 4
}
  1. 先透过 LocalLifecycleOwner 可以取得目前 Composable 所在 scope 的 LifecycleOwner
  2. 一样是透过 remember { mutableStateOf() } 将资料存放起来
  3. 透过一般的 LiveData.observe 去观察 LiveData 的资料,若有改动就将资料同步到 state
  4. 将 state 回传,给外部使用
  5. 为了确保当 LiveData or LifecycleOwner 有异动的时候能重新做 observe ,且除此之外不要做异动。使用之前提到过的 DisposableEffect
  6. onDispose 时,将 observer 回收

想不到 ViewModel 和 LiveData 在 Compose 中的使用其实非常的简单。在 LiveData 背後,又刚好是使用到之前所研究的 Side Effect API,感觉整个技术都串起来了!


Reference:


<<:  【Day 26】S3 on AWS Outpost 限制与建置流程

>>:  Day22:【技术篇】CSS 的变数运用技巧(1)

D15. 学习基础C、C++语言

D15. 字元阵列(2) 前一篇有讲到字元的输出是printf("%c",a[i...

[Day26] 重设密码API – views

哈罗大家好,今天要做的是重设密码API,先附上我的程序码~~ 程序码 @csrf_protect d...

Day22-TypeScript(TS)的函式(Function) Part2

昨天讲的将函式(Function)加入型别相信大家都了解了, 今天就带大家来看看**完整函式型别(W...

【PHP Telegram Bot】Day18 - 基础(7):逻辑运算与流程控制

前面介绍的程序都只有单一路径,今天要来讲更复杂的多路径的情况 运算子 比较运算子 说明 用法 ==...

ML专案的特徵工程为什麽存在?包含哪些层面?怎麽练手感?

在讨论MLOps的过程当中,许多客户会针对他们有兴趣的事情提出不同的问题,像是:模型监测、安全性、常...