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
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
keijumt
May 31, 2019
Technology
100
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Livedata Basics
keijumt
May 31, 2019
More Decks by keijumt
See All by keijumt
新規アプリのDarkTheme対応
keijumt
1
260
Firebase App Distribution と Github Actions でapkを配布する
keijumt
1
680
Other Decks in Technology
See All in Technology
もりもり新機能を一挙紹介! AgentCoreに入門して、AWS上にAIエージェントを構築しよう
minorun365
PRO
6
860
【Gen-AX】20260530開催_JJUG CCC 2026 Spring
genax
1
440
Rancherの紹介&Update情報(RancherJP Online Meetup #09)
yoshiyuki_kono
0
130
protovalidate-es を導入してみた
bengo4com
0
160
Socrates × Looker 〜セマンティックレイヤーで進化するデータ分析エージェント〜
hanon52_
2
1.5k
AI Engineering Summit Tokyo 2026 AIの前に、やることがある 〜医療データ企業の4フェーズ〜
dtaniwaki
0
2.3k
ChatworkとBPaaS 異なる特性で学んだAI機能開発の ベストプラクティス
kubell_hr
2
3.2k
Snowflakeと仲良くなる第一歩
coco_se
3
260
社内 AI エージェント Synapse と セマンティックレイヤーの育て方
hiroakis
0
910
Reliability in the Age of AI: Engineering for AI Velocity
rrreeeyyy
0
110
タクシーアプリ『GO』の実践的データ活用
mot_techtalk
3
180
Microsoft Build Keynoteふりかえり
tomokusaba
0
110
Featured
See All Featured
What does AI have to do with Human Rights?
axbom
PRO
1
2.2k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
200
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
530
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
380
First, design no harm
axbom
PRO
2
1.2k
The Cult of Friendly URLs
andyhume
79
6.9k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
122
22k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
140
The World Runs on Bad Software
bkeepers
PRO
72
12k
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
200
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.8k
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()) { } } } オブザーバは以前のものを解除していない
ありがとうございました