Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Level Up with LiveData (JP)

Level Up with LiveData (JP)

Android Dev Summit 2018 報告会

Daichi Furiya (Wasabeef)

December 10, 2018
Tweet

More Decks by Daichi Furiya (Wasabeef)

Other Decks in Programming

Transcript

  1. Observable Component1 Component2 class Component1( private val cmp2: Component2 )

    { "// ""... fun onDataLoaded(data: Data) { cmp2.setData(data) } } Component1 から Component2 へのデータを渡すシンプルな方法
  2. Observable ViewModel Activity class MyViewModel( private val activity: Activity )

    { "// ""... fun onDataLoaded(data: Data) { activity.setData(data) } } ViewModel が Activity を持つと何が悪いでしょうか?
  3. LiveData は Observable の一種 class MyViewModel : ViewModel() { val

    myObservable: LiveData<String> = "//""... } class MyActivity : Activity() { val viewModel: MyViewModel = "//""... override fun onCreate(""...) { viewModel.myObservable.observe(this, Observer { data "-> title = data.title }) } } 第二引数はオブザーバで、値が変更されたことを知ることができる
  4. Lifecycle-aware class MyActivity : Activity() { val viewModel: MyViewModel =

    "//""... override fun onCreate(""...) { viewModel.myObservable.observe(this, Observer { data "-> title = data.title }) } } 第一引数でどのライフサイクル(LifecycleOwner)に合わせるか
  5. Lifecycle-aware class MyActivity : Activity() { val viewModel: MyViewModel =

    "//""... override fun onCreate(""...) { viewModel.myObservable.observeForever { data "-> title = data.title } } } LiveData#observeForever ではアクティブかどうかは問わない テストで使うことが多い
  6. Transformations#map "// Transformations.java public static <X, Y> LiveData<Y> map( LiveData<X>

    source, final Function<X, Y> mapFunction) { ""... } map で他の LiveData に変換することが可能
  7. Transformations#map enum class Type { DOG, CAT, BIRD } data

    class Animal(val type: Type, val name: String) class AnimalRepository { fun getAnimals() = "//""... } class DogViewModel(private val repo: AnimalRepository) : ViewModel() { private val animals = MutableLiveData<List<Animal">>() val dogs: LiveData<List<Animal">> = Transformations.map(animals) { list "-> list.filter { animal "-> animal.type "== Type.DOG } } fun getAnimals() { animals.postValue(repo.getAnimals()) } } 簡単なサンプル
  8. Transformations#map enum class Type { DOG, CAT, BIRD } data

    class Animal(val type: Type, val name: String) class AnimalRepository { fun getAnimals() = "//""... } class DogViewModel(private val repo: AnimalRepository) : ViewModel() { private val animals = MutableLiveData<List<Animal">>() val dogs: LiveData<List<Animal">> = Transformations.map(animals) { list "-> list.filter { animal "-> animal.type "== Type.DOG } } fun getAnimals() { animals.postValue(repo.getAnimals()) } } 例えば、データソースから取得できる Animal のデータがあるとします
  9. Transformations#map enum class Type { DOG, CAT, BIRD } data

    class Animal(val type: Type, val name: String) class AnimalRepository { fun getAnimals() = "//""... } class DogViewModel(private val repo: AnimalRepository) : ViewModel() { private val animals = MutableLiveData<List<Animal">>() val dogs: LiveData<List<Animal">> = Transformations.map(animals) { list "-> list.filter { animal "-> animal.type "== Type.DOG } } fun getAnimals() { animals.postValue(repo.getAnimals()) } } AnimalRepository を扱う DataViewModel に animals 変数を用意
  10. Transformations#map enum class Type { DOG, CAT, BIRD } data

    class Animal(val type: Type, val name: String) class AnimalRepository { fun getAnimals() = "//""... } class DogViewModel(private val repo: AnimalRepository) : ViewModel() { private val animals = MutableLiveData<List<Animal">>() val dogs: LiveData<List<Animal">> = Transformations.map(animals) { list "-> list.filter { animal "-> animal.type "== Type.DOG } } fun getAnimals() { animals.postValue(repo.getAnimals()) } } Repository を使ってデータを取得し、animals 変数を更新
  11. Transformations#map enum class Type { DOG, CAT, BIRD } data

    class Animal(val type: Type, val name: String) class AnimalRepository { fun getAnimals() = "//""... } class DogViewModel(private val repo: AnimalRepository) : ViewModel() { private val animals = MutableLiveData<List<Animal">>() val dogs: LiveData<List<Animal">> = Transformations.map(animals) { list "-> list.filter { animal "-> animal.type "== Type.DOG } } fun getAnimals() { animals.postValue(repo.getAnimals()) } } animals から dog だけのデータを管理したい
  12. Transformations#map enum class Type { DOG, CAT, BIRD } data

    class Animal(val type: Type, val name: String) class AnimalRepository { fun getAnimals() = "//""... } class DogViewModel(private val repo: AnimalRepository) : ViewModel() { private val animals = MutableLiveData<List<Animal">>() val dogs: LiveData<List<Animal">> = Transformations.map(animals) { list "-> list.filter { animal "-> animal.type "== Type.DOG } } fun getAnimals() { animals.postValue(repo.getAnimals()) } } Type によってフィルタリングし、DOG だけにする
  13. Transformations#map enum class Type { DOG, CAT, BIRD } data

    class Animal(val type: Type, val name: String) class AnimalRepository { fun getAnimals() = "//""... } class DogViewModel(private val repo: AnimalRepository) : ViewModel() { private val animals = MutableLiveData<List<Animal">>() val dogs: LiveData<List<Animal">> = Transformations.map(animals) { list "-> list.filter { animal "-> animal.type "== Type.DOG } } fun getAnimals() { animals.postValue(repo.getAnimals()) } } 簡単なサンプルでした
  14. Transformations#switchMap Transformations#map で他の LiveData に変換することが可能 "// Transformations.java public static <X,

    Y> LiveData<Y> switchMap( LiveData<X> source, final Function<X, LiveData<Y">> switchMapFunction) { ""... }
  15. Transformations#switchMap class MyViewModel : ViewModel() { val repositoryResult = Transformations.switchMap(userManager.user)

    { user "-> repository.getDataForUser(user) } } 簡単に描くとこういう書き方になります
  16. class SharedLiveDataSource(val dataSource: MyDataSource) { fun loadDataForUser(userId: String): LiveData<Long> {

    val result = MutableLiveData<Long>() result.value = dataSource.getOnlineTime(userId) return result } } これだけ見ると特に問題なさそう... 2. LiveData を Activity 間で共有する
  17. class MyViewModel(private val repo: AnimalRepository) : ViewModel() { var animals

    = MutableLiveData<List<Animal">>() fun getAnimals() { animals = Transformations.switchMap(repo.getAnimals()) { it } } } オブザーバは新しいものが割り当てられたと判断できない 再登録が必要になるし、以前のものを解除していない 3. Transformation を初期化時以外でインスタンス化する
  18. class DogViewModel(private val repo: AnimalRepository) : ViewModel() { private val

    animals = MutableLiveData<List<Animal">>() val dogs: LiveData<List<Animal">> = Transformations.map(animals) { list "-> list.filter { animal "-> animal.type "== Type.DOG } } fun getAnimals() { animals.postValue(repo.getAnimals()) } } LiveData を var で定義しないことが大事 3. Transformation を初期化時以外でインスタンス化する
  19. 1. 大きいオブジェクトを Transformations で使う 2. LiveData を Activity 間で共有する 3.

    Transformation を初期化時以外でインスタンス化する アンチパターン
  20. Resources Talk sessions: 
 - https://www.youtube.com/playlist?list=PLWz5rJ2EKKc8WFYCR9esqGGY0vOZm2l6e Web pages: 
 -

    https://medium.com/androiddevelopers/search?q=livedata Photos: - https://unsplash.com - https://www.pexels.com - http://www.iconarchive.com - https://www.irasutoya.com