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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
keijumt
May 31, 2019
Technology
0
93
Livedata Basics
keijumt
May 31, 2019
Tweet
Share
More Decks by keijumt
See All by keijumt
新規アプリのDarkTheme対応
keijumt
1
260
Firebase App Distribution と Github Actions でapkを配布する
keijumt
1
670
Other Decks in Technology
See All in Technology
ナレッジワークのご紹介(第88回情報処理学会 )
kworkdev
PRO
0
180
AIエージェント、 社内展開の前に知っておきたいこと
oracle4engineer
PRO
2
100
8万デプロイ
iwamot
PRO
2
230
楽しく学ぼう!コミュニティ入門 AWSと人が つむいできたストーリー
hiroramos4
PRO
1
190
堅牢.py#2 LT資料
t3tra
0
130
Claude Codeが爆速進化してプラグイン追従がつらいので半自動化した話 ver.2
rfdnxbro
0
500
Oracle Database@AWS:サービス概要のご紹介
oracle4engineer
PRO
3
1.7k
非情報系研究者へ送る Transformer入門
rishiyama
11
7.1k
The_Evolution_of_Bits_AI_SRE.pdf
nulabinc
PRO
0
120
楽しく学ぼう!ネットワーク入門
shotashiratori
3
2.9k
GitLab Duo Agent Platform + Local LLMサービングで幸せになりたい
jyoshise
0
290
製造業ドメインにおける LLMプロダクト構築: 複雑な文脈へのアプローチ
caddi_eng
1
550
Featured
See All Featured
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.6k
The Curious Case for Waylosing
cassininazir
0
270
Ruling the World: When Life Gets Gamed
codingconduct
0
170
Practical Orchestrator
shlominoach
191
11k
Test your architecture with Archunit
thirion
1
2.2k
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
410
Utilizing Notion as your number one productivity tool
mfonobong
4
250
30 Presentation Tips
portentint
PRO
1
250
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
1.8k
How Software Deployment tools have changed in the past 20 years
geshan
0
32k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.4k
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()) { } } } オブザーバは以前のものを解除していない
ありがとうございました