再考:
監視可能オブジェクト
YUMEMI.grow Mobile #12
mikan(
一瀬喜弘)
Slide 2
Slide 2 text
自己紹介
object Mikan {
val name = "
一瀬喜弘"
val company = "karabiner.tech"
val work = Engineer.Android
val hobby = listOf(
"
漫画",
"
アニメ",
"
ゲーム",
"
折り紙",
"OSS
開発・コントリビュート",
)
}
Slide 3
Slide 3 text
Android
アプリのGUI
アーキテク
チャはなにを使っていますか?
Slide 4
Slide 4 text
きっとMVVM
と答える方が多い
んじゃないでしょうか
Slide 5
Slide 5 text
MVVM
をMVVM
たらしめてるも
のってなんでしょうか?
Slide 6
Slide 6 text
そうです。V
とVM
間のデータの
同期ですよね
Slide 7
Slide 7 text
ではデータの同期にはどのよう
な技術が使われているでしょ
か?
Slide 8
Slide 8 text
そう。監視可能オブジェクトで
すよね
Slide 9
Slide 9 text
(Observable) / LiveData /
StateFlow / State
Slide 10
Slide 10 text
どれをつかったらいいの?
Slide 11
Slide 11 text
この
発表では LiveData /
StateFlow / State
の
比較を
します
Slide 12
Slide 12 text
インスタンスの作り方
Slide 13
Slide 13 text
// LiveData
data class MainUiState(val name: String)
class MainViewModel : ViewModel() {
//
バッキングプロパティによってイミュータブルな方を外部に公開する
private val _uiModel = MutableLiveData()
val uiModel: LiveData = _uiModel
}
Slide 14
Slide 14 text
// StateFlow
data class MainUiState(val name: String)
class MainViewModel : ViewModel() {
//
バッキングプロパティによってイミュータブルな方を外部に公開する
private val _uiModel = MutableStateFlow(MainUiModel("")) //
初期値が必要
val uiModel = _uiModel.asStateFlow() //
型の明示が不要になる
}
Slide 15
Slide 15 text
// State
data class MainUiState(val name: String)
class MainViewModel : ViewModel() {
//
バッキングプロパティしなくてよい
var uiModel by mutableStateOf(MainUiModel(""))
private set
}
// 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
}
}
Slide 28
Slide 28 text
// State
//
おそらくSnapshot
システムを介して監視できるのかもしれない。
。
Slide 29
Slide 29 text
監視の仕方 from Compose
Slide 30
Slide 30 text
// LiveData
@Composable
fun MainScreen(
viewModel: MainViewModel = viewModel()
) {
// MainUiModel?
val uiModel by viewModel.name.observeAsState()
// MainUiModel
val uiModel by viewModel.name.observeAsState(MainUiModel(""))
}
Slide 31
Slide 31 text
// StateFlow
@Composable
fun MainScreen(
viewModel: MainViewModel = viewModel()
) {
// MainUiModel
val uiModel by viewModel.name.collectAsState()
//
バックグラウンドでStateFlow
が更新されてもrecomposition
を発生させない
val uiModel by viewModel.name.collectAsStateWithLifecycle()
}
Slide 32
Slide 32 text
// State
@Composable
fun MainScreen(
viewModel: MainViewModel = viewModel()
) {
val uiModel = viewModel.uiModel
}
// 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)
}
}
}
Slide 37
Slide 37 text
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
field = MutableLiveData()
val uiModel = field.asStateFlow()
field = MutableStateFlow(MainUiModel(""))
val uiModel: StateFlow<_>
field = MutableStateFlow(MainUiModel(""))
}