Upgrade to Pro — share decks privately, control downloads, hide ads and more …

再考: 監視可能オブジェクト

mikan
April 10, 2024

再考: 監視可能オブジェクト

mikan

April 10, 2024
Tweet

More Decks by mikan

Other Decks in Technology

Transcript

  1. 自己紹介 object Mikan { val name = " 一瀬喜弘" val

    company = "karabiner.tech" val work = Engineer.Android val hobby = listOf( " 漫画", " アニメ", " ゲーム", " 折り紙", "OSS 開発・コントリビュート", ) }
  2. // LiveData data class MainUiState(val name: String) class MainViewModel :

    ViewModel() { // バッキングプロパティによってイミュータブルな方を外部に公開する private val _uiModel = MutableLiveData<MainUiModel>() val uiModel: LiveData<MainUiModel> = _uiModel }
  3. // StateFlow data class MainUiState(val name: String) class MainViewModel :

    ViewModel() { // バッキングプロパティによってイミュータブルな方を外部に公開する private val _uiModel = MutableStateFlow(MainUiModel("")) // 初期値が必要 val uiModel = _uiModel.asStateFlow() // 型の明示が不要になる }
  4. // State data class MainUiState(val name: String) class MainViewModel :

    ViewModel() { // バッキングプロパティしなくてよい var uiModel by mutableStateOf(MainUiModel("")) private set }
  5. // LiveData class MainViewModel : ViewModel() { // ... fun

    updateName(newName) { // メインスレッドで実行する場合 _uiModel.value = MainUiMode(newName) // バックグラウンドスレッドで実行する場合 _uiModel.postValue(MainUiMode(newName)) } }
  6. // StateFlow class MainViewModel : ViewModel() { // ... fun

    updateName(newName) { // 値を評価 _uiModel.value = _uiModel.value.copy(newName) // 式を評価 viewModelScope.launch { _uiModel.update { it.copy(newName) } } } }
  7. // State class MainViewModel : ViewModel() { // ... fun

    updateName(newName) { uiModel = uiModel.copy(newName) } }
  8. // LiveData class MainActivity : AppCompatActivity() { private val viewModel:

    MainViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { viewModel.uiState.observe(this) { uiState -> // 更新された uiState を使って何かをする } } }
  9. // StateFlow class MainActivity : AppCompatActivity() { private val viewModel:

    MainViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { lifecycleScope.launch { viewModel.uiModel.collect { uiModel -> // 更新された uiModel を使って何かをする } } } }
  10. <!-- LiveData --> <!-- StateFlow --> <?xml version="1.0" encoding="utf-8"?> <layout

    xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="viewModel" type="com.github.mikanIchinose.app.MainViewModel" /> </data> </layout>
  11. // LiveData // StateFlow class MainActivity : AppCompatActivity() { private

    val viewModel: MainViewModel by viewModels() lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { binding = DataBindingUtil.setContentView( this, R.layout.activity_main ) binding.lifecycleOwner = this binding.viewmodel = viewModel } }
  12. // LiveData @Composable fun MainScreen( viewModel: MainViewModel = viewModel() )

    { // MainUiModel? val uiModel by viewModel.name.observeAsState() // MainUiModel val uiModel by viewModel.name.observeAsState(MainUiModel("")) }
  13. // StateFlow @Composable fun MainScreen( viewModel: MainViewModel = viewModel() )

    { // MainUiModel val uiModel by viewModel.name.collectAsState() // バックグラウンドでStateFlow が更新されてもrecomposition を発生させない val uiModel by viewModel.name.collectAsStateWithLifecycle() }
  14. LiveData StateFlow State インスタン スの作り方 バッキングプロパティを使って 安全に公開する必要がある バッキングプロパティを使って 安全に公開する必要がある setter

    をprivate にするだけ 更新の仕方 setValue とpostValue を使い分け る setValue とupdate を使い分ける 普通に変数を更新すればよい 監視の仕方 オブザーバーを使う 終端演算子を使う 勝手に監視される 使用難易度 スレッドのことを考えないとい けない null との戦い coroutine をよく知らないといけ ない Compose でしか使えないことを 除けば非常に簡単に使える
  15. // Setter しかもたないState はデータ競合しないのかといわれればそれはNo date class MainUiModel(val name: String, val

    age: Int) var uiModel by mutableStateOf(MainUiModel("", 0)) private set fun updateName(name: String) { viewModelScope.launch { withContext(Dispatchers.IO) { delay(1000) uiModel = uiModel.copy(name = name) } } } fun updateAge(age: Int) { viewModelScope.launch { withContext(Dispatchers.IO) { delay(1000) uiModel = uiModel.copy(age = age) } } }
  16. K2 コンパイラが正式リリースされたらExplicit Backing Fields によってバッキングプロパティはしなくてよくなる https://github.com/Kotlin/KEEP/blob/explicit-backing-fields-re/proposals/explicit-backing-fields.md#underscore-operator-in-type- parameters data class MainUiState(val

    name: String) class MainViewModel : ViewModel() { val uiModel: LiveData<MainUiModel> field = MutableLiveData<MainUiModel>() val uiModel = field.asStateFlow() field = MutableStateFlow(MainUiModel("")) val uiModel: StateFlow<_> field = MutableStateFlow(MainUiModel("")) }
  17. 複雑ではあるが coroutine を使って処理を実行することが多い ViewModel においては StateFlow を使った ほうが何かと便利 Flow の豊富なAPI

    が使える SavedStateHandle と相性がよい Compose のsavable API との統合もあるにはある(Snapshot システムについて知る必要があるが)