Save 37% off PRO during our Black Friday Sale! »

Livedata Basics

Livedata Basics

2f5ad50887405d93fc74a388e331fede?s=128

keijumt

May 31, 2019
Tweet

Transcript

  1. LiveDataの基本 松本圭樹

  2. LiveDataとは?

  3. ライフサイクルと連動した監視が 可能なデータホルダーのクラス

  4. Observerパターン + Lifecycle

  5. なぜLiveDataを利用するか?

  6. LiveDataを利用するメリット • メモリリークしない • Activityが非活性になったことによるクラッシュがなくなる • 常に最新のデータ • 構成の変更(画面回転など)の変更に適切に対処できる •

    ライフサイクルのハンドリングから開放される
  7. ライフサイクルと連動 • ライフサイクルがアクティブな時にのみ通知が送られる • ライフサイクルが終了するときにObserverが破棄される

  8. ライフサイクルがアクティブとは?

  9. Lifecycle.Stateが RESUMEDまたはSTARTED

  10. Lifecycle.State • CREATED • DESTROYED • INITIALIZED • RESUMED •

    STARTED
  11. Launch onCreate onStart onResume onPause onStop onDestroy Lifecycle State INITIALIZED

    CREATED STARTED RESUMED STARTED CREATED DESTROYED
  12. Launch onCreate onStart onResume onPause onStop onDestroy Lifecycle State INITIALIZED

    CREATED STARTED RESUMED STARTED CREATED DESTROYED ライフサイクルが アクティブ
  13. Launch onCreate onStart onResume onPause onStop onDestroy Lifecycle State INITIALIZED

    CREATED STARTED RESUMED STARTED CREATED DESTROYED ライフサイクルが インアクティブ
  14. Launch onCreate onStart onResume onPause onStop onDestroy Lifecycle State INITIALIZED

    CREATED STARTED RESUMED STARTED CREATED DESTROYED ライフサイクルが 破棄される
  15. 通常のObserverパターン Activity Subject 監視 変更 通知

  16. LiveData Activity LiveData 監視 変更 通知 Activity

  17. 実際にLiveDataを利用

  18. 例 Activity ViewModel LiveData 監視 通知

  19. ViewModel class MainViewModel { val name = MutableLiveData<String>() init {

    name.value = "Hoge" } }
  20. ViewModel class MainViewModel { val name = MutableLiveData<String>() init {

    name.value = "Hoge" } }
  21. ViewModel class MainViewModel { val name = MutableLiveData<String>() init {

    name.value = "Hoge" } } nameを監視しているObserverに通知を送信
  22. ViewModel class MainViewModel { val name = MutableLiveData<String>() init {

    name.postValue("Hoge") } } UIスレッド以外から通知を送る場合はpostValueを利用
  23. ViewModel class MainViewModel { val name = MutableLiveData<String>("Huga") init {

    name.value = "Hoge" } } 初期値を入れたい場合は第一引数に
  24. Activity class MainActivity : AppCompatActivity() { override fun onCreate( …

    ) { val viewModel = MainViewModel() viewModel.name.observe(this, Observer { name -> }) } }
  25. Activity class MainActivity : AppCompatActivity() { override fun onCreate( …

    ) { val viewModel = MainViewModel() viewModel.name.observe(this, Observer { name -> }) } }
  26. Activity class MainActivity : AppCompatActivity() { override fun onCreate( …

    ) { val viewModel = MainViewModel() viewModel.name.observe(this, Observer { name -> }) } }
  27. Activity class MainActivity : AppCompatActivity() { override fun onCreate( …

    ) { val viewModel = MainViewModel() viewModel.name.observe(this, Observer { name -> }) } } 第一引数はどのライフサイクル(LifecycleOwner)に合わせるか
  28. Activity class MainActivity : AppCompatActivity() { override fun onCreate( …

    ) { val viewModel = MainViewModel() viewModel.name.observe(this, Observer { name -> }) } } 第二引数はオブザーバ、通知を受けた時にここが呼ばれる
  29. Transformations

  30. Transformationsとは? • オブザーバに通知する前にLiveDataの値を変更したり、値を元に別のLiveData インスタンスに変換して返すことが可能

  31. Transformations • map • switchMap

  32. Transformations#map • オブザーバに通知する前にLiveDataの値を変更する

  33. 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() } }
  34. 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() } }
  35. 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() } }
  36. 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() } }
  37. 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を第一引数に設定
  38. 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() } } 受け取ったデータを整形して返却する
  39. Transformations#switchMap • 値を元に別のLiveDataインスタンスに変換して返すことが可能

  40. 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 } }
  41. 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 } }
  42. 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 } }
  43. 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 } }
  44. 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>>
  45. アンチパターン

  46. ViewでLiveDataを保持する

  47. ViewでLiveDataを保持する • Viewはデータ表示の役割はあるが、データ保持の役割はない • 通常はViewModelでLiveDataを保持する ◦ 画面回転などにも耐える

  48. MutableLiveDataをViewに公開

  49. MutableLiveDataをViewに公開 • ViewからViewModelのLiveDataを操作が可能になる

  50. バッキングプロパティを利用 class MainViewModel { private val _name = MutableLiveData<String>() val

    name: LiveData<String> = _name } 外部に公開するのはLiveData型のメンバ変数
  51. FragmentでLiveDataを購読時、第 一引数でthisを渡す

  52. なぜだめなのか? • Fragment#onDestroyが呼ばれることなく、複数回onCreateViewが呼ばれる可 能性がある ◦ 重複してObserverを登録することになる

  53. viewLifecycleOwnerを使う class MainFragment : Fragment() { override fun onCreateView( …

    ): View { val viewModel = MainViewModel() viewModel.name.observe(viewLifecycleOwner, Observer { }) return ... } }
  54. viewLifecycleOwnerを使う class MainFragment : Fragment() { override fun onCreateView( …

    ): View { val viewModel = MainViewModel() viewModel.name.observe(viewLifecycleOwner, Observer { }) return ... } }
  55. Toastの表示や、Activity遷移など のイベントでMutableLiveDataを 利用する

  56. Activityの再生成で挙動がおかしく なる

  57. Activityの再生成

  58. Activityの再生成 画面遷移を行うLiveDataから 通知を受ける

  59. Activityの再生成 画面遷移を行うLiveDataから 通知を受ける 再び画面遷移を行う LiveData から通知を受ける

  60. Activityの再生成 画面遷移を行うLiveDataから 通知を受ける 一度通知を受けたデータは通 知を受け取らない

  61. 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

  62. Transformationsを初期化時以外 で利用する

  63. Transformationsを初期化時以外で利用する class MainViewModel ( ... ){ var cats = MutableLiveData<List<Animal>>()

    fun getFilterName() { cats = Transformations.map(animalRepository.getAnimals()) { } } } オブザーバは以前のものを解除していない
  64. ありがとうございました