Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Livedata Basics
Search
keijumt
May 31, 2019
Technology
110
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Livedata Basics
keijumt
May 31, 2019
Transcript
64 slides
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()) { } } } オブザーバは以前のものを解除していない
ありがとうございました
More Decks by keijumt
See All by keijumt
新規アプリのDarkTheme対応
keijumt
1
270
Firebase App Distribution と Github Actions でapkを配布する
keijumt
1
690
Other Decks in Technology
See All in Technology
Kiro アップデート 2026 年 11 月号 (11 月 8 日時点) | 目指せ Kiro マスター アップデート編
inariku
PRO
2
120
大江戸Ruby会議でデータベース クライアントの話をするのは 間違っているだろうか
yahonda
1
120
MCPでネットワーク障害調査を自動化する前に直すべき3つのこと
yama3133
0
200
これからのデータエンジニアの役割について考える / Rethinking the Role of Data Engineers in the AI Era
okiyuki99
0
490
速くなるほど、深く考える AI時代のQAに学ぶ
kobayashiyorimitsu
0
210
Kiro CLI 入門 (ターミナルと CI で使う) | 目指せ Kiro マスター 03
inariku
PRO
2
150
複数システムが同居した 15 年物の AWS アカウントを解体するための考古学
wa6sn
3
150
Sonnetだけで業務はまわる?
kintotechdev
1
120
異夢同船〜異なる夢でも同じ船でなんとかする〜
fortegp05
0
230
AWS Quick 入門 | 目指せ Quick マスター 01
inariku
PRO
2
340
田んぼの見回りをIoTで減らしたい!水位監視から電池駆動カメラまでの試行錯誤
itosoft
1
190
Kiro Web 入門 (サインインから、頼み方、PR の受け取り、クラウドの決まりまで) | 目指せ Kiro マスター 04
inariku
PRO
2
120
Featured
See All Featured
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Building a Scalable Design System with Sketch
lauravandoore
464
34k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
790
How to Talk to Developers About Accessibility
jct
2
620
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
630
Rails Girls Zürich Keynote
gr2m
96
14k
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
1
610
The agentic SEO stack - context over prompts
schlessera
0
1k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6.2k
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
3
3.9k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
31k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k