Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Livedata Basics
Search
keijumt
May 31, 2019
Technology
0
82
Livedata Basics
keijumt
May 31, 2019
Tweet
Share
More Decks by keijumt
See All by keijumt
新規アプリのDarkTheme対応
keijumt
1
230
Firebase App Distribution と Github Actions でapkを配布する
keijumt
1
500
Other Decks in Technology
See All in Technology
KubeConにproposalを送りたい人へのアドバイス
sat
PRO
3
240
Azure犬駆動開発の記録/GlobalAzureFukuoka2024_20240420
nina01
1
210
20240416_devopsdaystokyo
kzkmaeda
1
220
よく聞くけど使ったことないソフトウェアNo.1 KafkaとSnowflake
foursue
4
350
一生覚えておきたい「システム開発=コミュニケーション」〜初めての実務案件振り返りLT〜
maimyyym
0
130
MySQL の SQL クエリチューニングの要所を掴む勉強会
andpad
2
6.2k
Next'24 事例セッションの紹介とクラウド資格を活用したキャリア形成について語りMuscle
yasumuusan
1
440
MLOpsの「壁」を乗り越える、LINEヤフーの Data Quality as Code
lycorptech_jp
PRO
5
500
Cracking the KubeCon CfP
inductor
2
240
元インフラエンジニアに成る / Human Resources to Human Relations
bobtani
4
910
Kernel MemoryでAzure OpenAI Serviceとお手軽データソース連携
mitsuzono
1
240
コンテナセキュリティの基本と脅威への対策
kyohmizu
3
760
Featured
See All Featured
VelocityConf: Rendering Performance Case Studies
addyosmani
320
23k
Six Lessons from altMBA
skipperchong
21
3k
For a Future-Friendly Web
brad_frost
172
9k
From Idea to $5000 a Month in 5 Months
shpigford
377
45k
GraphQLとの向き合い方2022年版
quramy
32
12k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
7
1k
Done Done
chrislema
178
15k
Ruby is Unlike a Banana
tanoku
96
10k
Git: the NoSQL Database
bkeepers
PRO
422
63k
Unsuck your backbone
ammeep
663
57k
Why You Should Never Use an ORM
jnunemaker
PRO
51
8.6k
Put a Button on it: Removing Barriers to Going Fast.
kastner
58
3k
Transcript
LiveDataの基本 松本圭樹
LiveDataとは?
ライフサイクルと連動した監視が 可能なデータホルダーのクラス
Observerパターン + Lifecycle
なぜLiveDataを利用するか?
LiveDataを利用するメリット • メモリリークしない • Activityが非活性になったことによるクラッシュがなくなる • 常に最新のデータ • 構成の変更(画面回転など)の変更に適切に対処できる •
ライフサイクルのハンドリングから開放される
ライフサイクルと連動 • ライフサイクルがアクティブな時にのみ通知が送られる • ライフサイクルが終了するときにObserverが破棄される
ライフサイクルがアクティブとは?
Lifecycle.Stateが RESUMEDまたはSTARTED
Lifecycle.State • CREATED • DESTROYED • INITIALIZED • RESUMED •
STARTED
Launch onCreate onStart onResume onPause onStop onDestroy Lifecycle State INITIALIZED
CREATED STARTED RESUMED STARTED CREATED DESTROYED
Launch onCreate onStart onResume onPause onStop onDestroy Lifecycle State INITIALIZED
CREATED STARTED RESUMED STARTED CREATED DESTROYED ライフサイクルが アクティブ
Launch onCreate onStart onResume onPause onStop onDestroy Lifecycle State INITIALIZED
CREATED STARTED RESUMED STARTED CREATED DESTROYED ライフサイクルが インアクティブ
Launch onCreate onStart onResume onPause onStop onDestroy Lifecycle State INITIALIZED
CREATED STARTED RESUMED STARTED CREATED DESTROYED ライフサイクルが 破棄される
通常のObserverパターン Activity Subject 監視 変更 通知
LiveData Activity LiveData 監視 変更 通知 Activity
実際にLiveDataを利用
例 Activity ViewModel LiveData 監視 通知
ViewModel class MainViewModel { val name = MutableLiveData<String>() init {
name.value = "Hoge" } }
ViewModel class MainViewModel { val name = MutableLiveData<String>() init {
name.value = "Hoge" } }
ViewModel class MainViewModel { val name = MutableLiveData<String>() init {
name.value = "Hoge" } } nameを監視しているObserverに通知を送信
ViewModel class MainViewModel { val name = MutableLiveData<String>() init {
name.postValue("Hoge") } } UIスレッド以外から通知を送る場合はpostValueを利用
ViewModel class MainViewModel { val name = MutableLiveData<String>("Huga") init {
name.value = "Hoge" } } 初期値を入れたい場合は第一引数に
Activity class MainActivity : AppCompatActivity() { override fun onCreate( …
) { val viewModel = MainViewModel() viewModel.name.observe(this, Observer { name -> }) } }
Activity class MainActivity : AppCompatActivity() { override fun onCreate( …
) { val viewModel = MainViewModel() viewModel.name.observe(this, Observer { name -> }) } }
Activity class MainActivity : AppCompatActivity() { override fun onCreate( …
) { val viewModel = MainViewModel() viewModel.name.observe(this, Observer { name -> }) } }
Activity class MainActivity : AppCompatActivity() { override fun onCreate( …
) { val viewModel = MainViewModel() viewModel.name.observe(this, Observer { name -> }) } } 第一引数はどのライフサイクル(LifecycleOwner)に合わせるか
Activity class MainActivity : AppCompatActivity() { override fun onCreate( …
) { val viewModel = MainViewModel() viewModel.name.observe(this, Observer { name -> }) } } 第二引数はオブザーバ、通知を受けた時にここが呼ばれる
Transformations
Transformationsとは? • オブザーバに通知する前にLiveDataの値を変更したり、値を元に別のLiveData インスタンスに変換して返すことが可能
Transformations • map • switchMap
Transformations#map • オブザーバに通知する前にLiveDataの値を変更する
Transformations#map class MainViewModel( … ) { val animals = MutableLiveData<List<Animal>>()
val cats : LiveData<List<Animal>> = Transformations.map(animals) { animals -> animals.filter { it.type == Animal.Type.Cat } } init { animals.value = animalRepository.getAnimals() } }
Transformations#map class MainViewModel( … ) { val animals = MutableLiveData<List<Animal>>()
val cats : LiveData<List<Animal>> = Transformations.map(animals) { animals -> animals.filter { it.type == Animal.Type.Cat } } init { animals.value = animalRepository.getAnimals() } }
Transformations#map class MainViewModel( … ) { val animals = MutableLiveData<List<Animal>>()
val cats : LiveData<List<Animal>> = Transformations.map(animals) { animals -> animals.filter { it.type == Animal.Type.Cat } } init { animals.value = animalRepository.getAnimals() } }
Transformations#map class MainViewModel( … ) { val animals = MutableLiveData<List<Animal>>()
val cats : LiveData<List<Animal>> = Transformations.map(animals) { animals -> animals.filter { it.type == Animal.Type.Cat } } init { animals.value = animalRepository.getAnimals() } }
Transformations#map class MainViewModel( … ) { val animals = MutableLiveData<List<Animal>>()
val cats : LiveData<List<Animal>> = Transformations.map(animals) { animals -> animals.filter { it.type == Animal.Type.Cat } } init { animals.value = animalRepository.getAnimals() } } 監視するLiveDataを第一引数に設定
Transformations#map class MainViewModel( … ) { val animals = MutableLiveData<List<Animal>>()
val cats : LiveData<List<Animal>> = Transformations.map(animals) { animals -> animals.filter { it.type == Animal.Type.Cat } } init { animals.value = animalRepository.getAnimals() } } 受け取ったデータを整形して返却する
Transformations#switchMap • 値を元に別のLiveDataインスタンスに変換して返すことが可能
Transformations#switchMap class MainViewModel( … ) { val type = MutableLiveData<Animal.Type>()
val filterdAnimals : LiveData<List<Animal>> = Transformations.switchMap(type) { type -> animalRepository.getAnimalsByType(type) } init { animalType.value = Animal.Type.Cat } }
Transformations#switchMap class MainViewModel( … ) { val type = MutableLiveData<Animal.Type>()
val filterdAnimals : LiveData<List<Animal>> = Transformations.switchMap(type) { type -> animalRepository.getAnimalsByType(type) } init { animalType.value = Animal.Type.Cat } }
Transformations#switchMap class MainViewModel( … ) { val type = MutableLiveData<Animal.Type>()
val filterdAnimals : LiveData<List<Animal>> = Transformations.switchMap(type) { type -> animalRepository.getAnimalsByType(type) } init { animalType.value = Animal.Type.Cat } }
Transformations#switchMap class MainViewModel( … ) { val type = MutableLiveData<Animal.Type>()
val filterdAnimals : LiveData<List<Animal>> = Transformations.switchMap(type) { type -> animalRepository.getAnimalsByType(type) } init { animalType.value = Animal.Type.Cat } }
Transformations#switchMap class MainViewModel( … ) { val type = MutableLiveData<Animal.Type>()
val filterdAnimals : LiveData<List<Animal>> = Transformations.switchMap(type) { type -> animalRepository.getAnimalsByType(type) } init { animalType.value = Animal.Type.Cat } } getAnimalsByTypeの戻り値はLiveData<List<Animal>>
アンチパターン
ViewでLiveDataを保持する
ViewでLiveDataを保持する • Viewはデータ表示の役割はあるが、データ保持の役割はない • 通常はViewModelでLiveDataを保持する ◦ 画面回転などにも耐える
MutableLiveDataをViewに公開
MutableLiveDataをViewに公開 • ViewからViewModelのLiveDataを操作が可能になる
バッキングプロパティを利用 class MainViewModel { private val _name = MutableLiveData<String>() val
name: LiveData<String> = _name } 外部に公開するのはLiveData型のメンバ変数
FragmentでLiveDataを購読時、第 一引数でthisを渡す
なぜだめなのか? • Fragment#onDestroyが呼ばれることなく、複数回onCreateViewが呼ばれる可 能性がある ◦ 重複してObserverを登録することになる
viewLifecycleOwnerを使う class MainFragment : Fragment() { override fun onCreateView( …
): View { val viewModel = MainViewModel() viewModel.name.observe(viewLifecycleOwner, Observer { }) return ... } }
viewLifecycleOwnerを使う class MainFragment : Fragment() { override fun onCreateView( …
): View { val viewModel = MainViewModel() viewModel.name.observe(viewLifecycleOwner, Observer { }) return ... } }
Toastの表示や、Activity遷移など のイベントでMutableLiveDataを 利用する
Activityの再生成で挙動がおかしく なる
Activityの再生成
Activityの再生成 画面遷移を行うLiveDataから 通知を受ける
Activityの再生成 画面遷移を行うLiveDataから 通知を受ける 再び画面遷移を行う LiveData から通知を受ける
Activityの再生成 画面遷移を行うLiveDataから 通知を受ける 一度通知を受けたデータは通 知を受け取らない
SingleLiveEvent • 同じデータは通知を受けることのないLiveDataを作成する • https://github.com/googlesamples/android-architecture/blob/dev-todo-m vvm-live/todoapp/app/src/main/java/com/example/android/architecture/ blueprints/todoapp/SingleLiveEvent.java
Transformationsを初期化時以外 で利用する
Transformationsを初期化時以外で利用する class MainViewModel ( ... ){ var cats = MutableLiveData<List<Animal>>()
fun getFilterName() { cats = Transformations.map(animalRepository.getAnimals()) { } } } オブザーバは以前のものを解除していない
ありがとうございました