Lifecycle, ViewModel, LiveData の復習

Lifecycle, ViewModel, LiveData の復習

Android Architecture Components 勉強会

D2bcabeeb1ddff142fb8988b412cb4d3?s=128

Yuki Anzai

March 25, 2019
Tweet

Transcript

  1. Architecture Components 勉勉強会 あんざいゆき えがわ わさびーふ あらき 第5回⽬目 Lifecycle, ViewModel,

    LiveData の復復習
  2. Architecture Components 勉強会 とは • Architecture Components の基礎知識について学ぶ勉強会 • 主催:GDG

    Tokyo ハッシュタグ: #gdgtokyo • 講師、チューター:Google 社員と Google Developer Expert • (あんざいゆき、 えがわ、わさびーふ、あらき) • 当⽇チューター : @itome, @takahirom, @mokelab, @zaki50
  3. Architecture Components 勉強会 とは • 計4回の予定 • 第5回: Lifecycle, ViewModel,

    LiveData の復習 • 第6回: Room の復習, Paging • 第7回: WorkManager • 第8回: Navigation タイムテーブル 19:30~19:35 挨拶&説明 19:35~20:15 説明 20:15~20:25 休憩 20:25~ 課題取り組み
  4. Architecture Components とは 複数の機能・ライブラリの総称 • Data Binding • Lifecycles •

    LiveData • ViewModel • Room • Paging • WorkManager • Navigation
  5. Architecture Components とは 複数の機能・ライブラリの総称 • Data Binding • Lifecycles •

    LiveData • ViewModel • Room • Paging • WorkManager • Navigation 個別利⽤OK 組み合わせ利⽤OK
  6. Architecture Components とは 複数の機能・ライブラリの総称 • Data Binding • Lifecycles •

    LiveData • ViewModel • Room • Paging • WorkManager • Navigation 個別利⽤OK 組み合わせ利⽤OK #1 #2 #3 #4
  7. Architecture Components とは 複数の機能・ライブラリの総称 • Data Binding • Lifecycles •

    LiveData • ViewModel • Room • Paging • WorkManager • Navigation 個別利⽤OK 組み合わせ利⽤OK #1 #2 #3 #4 今⽇は これの 復習
  8. Architecture Components とは 複数の機能・ライブラリの総称 • Data Binding • Lifecycles •

    LiveData • ViewModel • Room • Paging • WorkManager • Navigation 個別利⽤OK 組み合わせ利⽤OK #1 #2 #3 #4 今⽇は これの 復習 #6 #7 #8
  9. Architecture Components の⽬的 堅牢 (robust) で テストしやすく (testable) 保守しやすい (maintainable)

    アプリの設計ができるように⼿助けする
  10. 復習

  11. Lifecycles

  12. Lifecycles • Lifecycles = Lifecycle-aware Components • ライフサイクルを感知するコンポーネント • Activity

    や Fragment のライフサイクル状態が変わったときに何 かアクションを起こす、ということができるようになる
  13. Lifecycle.State • 現在のライフサイクル状態を表す enum • INITIALIZED • DESTROYED • CREATED

    • STARTED • RESUMED https://developer.android.com/topic/libraries/architecture/lifecycle.html
  14. Lifecycle.Event • ライフサイクル状態が変わった時のイベントを表す enum https://developer.android.com/topic/libraries/architecture/lifecycle.html • ON_CREATE • ON_START •

    ON_RESUME • ON_PAUSE • ON_STOP • ON_DESTROY • ON_ANY
  15. Lifecycle val state = lifecycle.currentState 現在のライフサイクル状態を取得する val isAtLeastResumed = lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)

    現在のライフサイクル状態が xx より後であるかどうか
  16. LifecycleOwner • クラスが Lifecycle を持つことを表す single method interface • FragmentActivity

    の親クラスである ComponentActivity が LifecycleOwner を実装している • Fragment は LifecycleOwner を実装している public interface LifecycleOwner { @NonNull Lifecycle getLifecycle(); }
  17. Lifecycle を取得する class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState:

    Bundle?) { super.onCreate(savedInstanceState) val lifecycle = lifecycle } } class MainFragment : Fragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) val lifecycle = lifecycle } }
  18. LifecycleObserver • Lifecycle に追加・削除できるオブザーバーを表す interface • メソッドに @OnLifecycleEvent アノテーションをつけると、対応する Lifecycle.Event

    が発⾏されたときにメソッドが呼ばれる public interface LifecycleObserver { }
  19. OnLifecycleEvent • LifecycleObserver のメソッドにつけるアノテーション • LifecycleEvent を指定する val observer: LifecycleObserver

    = object : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onStart() { } }
  20. OnLifecycleEvent • 第1引数で LifecycleOwner を受け取ることができる • ON_ANY のみ第2引数で Lifecycle.Event を受け取ることができる

    val observer: LifecycleObserver = object : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onStart(source: LifecycleOwner) { } @OnLifecycleEvent(Lifecycle.Event.ON_ANY) fun onAny(source: LifecycleOwner, event: Lifecycle.Event) { } }
  21. LifecycleService • LifecycleOwner が実装された Service class MyService : LifecycleService() {

    override fun onCreate() { super.onCreate() val lifecycle = lifecycle } }
  22. ProcessLifecycleOwner • アプリのプロセス全体に対する Lifecycle を提供する • ON_CREATE は⼀度だけ発⾏される • ON_DESTROY

    は発⾏されない ProcessLifecycleOwner.get() .lifecycle .addObserver(observer)
  23. ProcessLifecycleOwner • ON_START, ON_RESUME は、最初の Activity がそのイベントを通るとき に発⾏される • ON_STOP,

    ON_PAUSE は、最後の Activity がそのイベントを通るときに遅 延を伴って発⾏される • コンフィギュレーションチェンジによる再⽣成時は発⾏されない * 遅延は destruction 時のみ
  24. LiveData

  25. 主な LiveData • LiveData • 値の変更を Observe できるデータホルダー • MutableLiveData

    • 外部から変更可能な LiveData • MediatorLiveData • 複数の LiveData を束ねて管理する MutableLiveData
  26. 値の変更を Observe する • LifecycleOwner の Lifecycle が • アクティブのときに値が変更された

    → すぐに通知される • アクティブじゃないときに値が変更された → 次にアクティブになった ときに通知される @MainThread public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)
  27. 値の変更を Observe する • 値が変更された → すぐに通知される @MainThread public void

    observeForever(@NonNull Observer<? super T> observer)
  28. 現在の値を取得する • ⼀度も値がセットされていないときは null が返る public T getValue()

  29. active / inactive • アクティブな Observer の数が1以上になったときに呼ばれる protected void onActive()

    protected void onInactive() • アクティブな Observer の数が1未満になったときに呼ばれる
  30. Observer • LiveData からの値を受け取るシンプルなインタフェース • LiveData から値が通知される = onChanged() が呼ばれる

    public interface Observer<T> { void onChanged(T t); }
  31. MutableLiveData • LiveData では値を変更するメソッドは protected • MutableLiveData では値を変更するメソッドが public になっている

    public void setValue(T value) public void postValue(T value) Main スレッド外から値を設定する際の便利メソッド 値を設定する(Main スレッドで呼び出すこと)
  32. MediatorLiveData • 複数の LiveData を束ねて管理できる • 型の異なる複数の LiveData にイベントを伝播させることができる @MainThread

    public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) @MainThread public <S> void removeSource(@NonNull LiveData<S> toRemote)
  33. Transformations • MediatorLiveData を使いやすくしたユーティリティ @MainThread public static <X, Y> LiveData<Y>

    map( @NonNull LiveData<X> source, @NonNull final Function<X, Y> mapFunction) @MainThread public static <X, Y> LiveData<Y> switchMap( @NonNull LiveData<X> source, @NonNull final Function<X, LiveData<Y>> switchMapFunction)
  34. ViewModel

  35. ViewModel • Activity の画⾯回転時のデータ保持 • Activity の複数 Fragment 間でのデータ受け渡し •

    LiveData と併⽤することが多い • プロセス停⽌後は復旧できない • データの永続化ではない
  36. ViewModel

  37. liveData.observe(lifecycleOwner, observer) ViewModel + LiveData

  38. ViewModel の定義 • ViewModel または AndroidViewModel を継承する class MyViewModel :

    ViewModel() { } class MyViewModel(private val app: Application) : AndroidViewModel(app) { }
  39. リソースの解放 • onCleared() で不要になったリソースを解放してリークを防⽌する class MainViewModel : ViewModel() { override

    fun onCleared() { super.onCleared() // ここでリソースを解放する } }
  40. ViewModel のインスタンス化 val viewModel: T = ViewModelProviders .of(<fragmentActivity || fragment>,

    factory) .get(<T : ViewModel>::class.java) • ViewModelProviders.of() と ViewModelProvider.get() を使う • ⾃分で new しない! val viewModel: MainViewModel = ViewModelProviders .of(this) .get(MainViewModel::class.java)
  41. ViewModelProvider.Factory • ViewModelProviders.of() の第2引数に指定する public interface Factory { @NonNull <T

    extends ViewModel> T create(@NonNull Class<T> modelClass); } val factory: ViewModelProvider.Factory = ... val viewModel: MainViewModel = ViewModelProviders .of(this, factory) .get(MainViewModel::class.java)
  42. What's new in 2.1.0-alpha

  43. LifecycleEventObserver • 全ての lifecycle change を受け取れる interface https://developer.android.com/reference/androidx/lifecycle/LifecycleEventObserver lifecycle.addObserver(LifecycleEventObserver {

    source, event -> println("${source.lifecycle.currentState} : $event") })
  44. distinctUntilChanged • 元 の LiveData の値が変わるまで emit しない LiveData を返す

    Transformations.distinctUntilChanged(liveData) .observe(this, Observer { ... }) https://developer.android.com/reference/androidx/lifecycle/Transformations.html#distinctUntilChanged(androidx.lifecycle.LiveData%3CX%3E)
  45. ktx extensions implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.1.0-alpha03" viewModel.liveData.map { ... } viewModel.liveData.switchMap {

    ... } viewModel.liveData.distinctUntilChanged() viewModel.liveData.observe(this) { ... }
  46. ViewModel.viewModelScope • lifecycle-viewmodel-ktx • ViewModel に紐づいた CoroutineScope • Dispatchers.Main val

    ViewModel.viewModelScope: CoroutineScope get() { val scope: CoroutineScope? = this.getTag(JOB_KEY) if (scope != null) { return scope } return setTagIfAbsent(JOB_KEY, CloseableCoroutineScope(Job() + Dispatchers.Main)) } implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0-alpha03"
  47. ViewModel Savedstate https://developer.android.com/reference/androidx/lifecycle/SavedStateVMFactory val savedMainViewModel = ViewModelProviders.of(this, SavedStateVMFactory(this)) .get(SavedMainViewModel::class.java) button.setOnClickListener

    { savedMainViewModel.update() } savedMainViewModel.liveData.observe(this, Observer { println(it) }) class SavedMainViewModel(private val handle: SavedStateHandle) : ViewModel() { private val _liveData = handle.getLiveData<Long>("time") val liveData: LiveData<Long> get() = _liveData fun update() { _liveData.value = System.currentTimeMillis() } }
  48. 課題

  49. Lifecycles

  50. 課題 0.1 • Empty Activity で新しいプロジェクトを作る • com.example.aacstudy • Kotlin

    • AndroidX
  51. 課題 0.2 apply plugin: 'kotlin-kapt' ... dependencies { ... implementation

    'androidx.appcompat:appcompat:1.1.0-alpha03' ... def lifecycle_version = "2.0.0" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version" testImplementation "androidx.arch.core:core-testing:$lifecycle_version" } app/build.gradle
  52. 課題 1.1 • Activity の各ライフサイクルメソッドで Lifecycle の currentState を確認 する

    • onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroy(), onRestart(), onSaveInstanceState() class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... println("onCreate : ${lifecycle.currentState.name}") } }
  53. None
  54. 課題 2.1 • LifecycleObserver の実装を⽤意して Lifecycle に登録する val lifecycleObserver =

    object : LifecycleObserver { } lifecycle.addObserver(lifecycleObserver)
  55. 課題 2.2 • 各 Lifecycle.Event を @OnLifecycleEvent に指定したメソッドを定義し、 呼び出されたときにログに出⼒する val

    lifecycleObserver = object : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) fun onCreate(source: LifecycleOwner) { println("ON_CREATE : ${source.lifecycle.currentState.name}") } ... @OnLifecycleEvent(Lifecycle.Event.ON_ANY) fun onAny(source: LifecycleOwner, event: Lifecycle.Event) { println("ON_ANY : ${source.lifecycle.currentState.name}") } }
  56. 課題 2.3 • ON_STOP 時に observer を解除すると、ON_DESTROY 時のメソッドが呼 ばれなくなるのを確認する •

    val lifecycleObserver = object : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onStop(source: LifecycleOwner) { source.lifecycle.removeObserver(this) } @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroy(source: LifecycleOwner) { println("ON_DESTROY : ${source.lifecycle.currentState.name}") } }
  57. 課題 4.0 • Application を継承した MainApplication を⽤意する • MainApplication を

    AndroidManifest.xml に設定する class MainApplication : Application() { } <application android:name=".MainApplication" ...> ... </application>
  58. 課題 4.1 • MainApplication の onCreate() で LifecycleObserver の実装を⽤意し、 ProcessLifecycleOwner

    の Lifecycle に登録する class MainApplication : Application() { override fun onCreate() { super.onCreate() val lifecycleObserver = object : LifecycleObserver { } ProcessLifecycleOwner.get() .lifecycle .addObserver(lifecycleObserver) } }
  59. 課題 4.2 • LifecycleObserver に @OnLifecycleEvent に ON_ANY を指定したメソッ ド⽤意する

    val lifecycleObserver = object : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_ANY) fun onAny(source: LifecycleOwner, event: Lifecycle.Event) { println("ON_ANY : ${source.lifecycle.currentState.name} $event") } }
  60. 課題 4.3 • いろんな操作をしたときのログを確認する • ホームキーをタップしたとき • Recent Apps から復帰したとき

    • 別の Activity に遷移したとき • 別の Activity から戻ってきたとき • 画⾯回転したとき
  61. • バックキーでアプリを終了したあと、ランチャーや RecentApps から起動 したとき ProcessLifecycleOwner で ON_CREATE は呼ばれる? •

    Recent Apps からクリアーしたあと、ランチャーから起動したとき ProcessLifecycleOwner で ON_CREATE は呼ばれる?
  62. LiveData

  63. 課題 5.1 • ボタンをタップしたとき、どのような順番で Observer に通知されるか確 認する val liveData =

    MutableLiveData<String>() liveData.observe(this, Observer { println(it) }) button.setOnClickListener { liveData.value = "a" liveData.value = "b" }
  64. 課題 5.2 • ボタンをタップしたとき、どのような順番で Observer に通知されるか確 認する val liveData =

    MutableLiveData<String>() liveData.observe(this, Observer { println(it) }) button.setOnClickListener { liveData.postValue("a") liveData.value = "b" }
  65. 課題 5.3 • ボタンをタップしたとき、どのような順番で Observer に通知されるか確 認する val liveData =

    MutableLiveData<String>() liveData.observe(this, Observer { println(it) }) button.setOnClickListener { liveData.postValue("a") liveData.postValue("b") }
  66. 課題6.1 • LiveData を継承した MainLiveData を⽤意し、onActive() と onInactive() で hasActiveObservers()

    の結果を確認する class MainLiveData : LiveData<String>() { override fun onActive() { super.onActive() println("onActive : ${hasActiveObservers()}") } override fun onInactive() { super.onInactive() println("onInactive : ${hasActiveObservers()}") } }
  67. 課題6.2 • MainActivity の onCreate() で MainLiveData を⽣成する • MainLiveData

    を observe し、いろんな操作をしたときのログを確認する • ホームキーをタップしたとき • Recent Apps から復帰したとき • 別の Activity に遷移したとき • 別の Activity から戻ってきたとき • 画⾯回転したとき
  68. 課題6.3 • ToggleButton を⽤意し、ボタンのチェックが切り替わったら MainLiveData を observe/removeObservers する • observe/removeObservers

    したときのログを確認する val liveData = MainLiveData() toggleButton.setOnCheckedChangeListener { _, isChecked -> if (isChecked) { liveData.observe(this, Observer { println(it) }) } else { liveData.removeObservers(this) } }
  69. 課題7.1 • CountUpLiveData を⽤意する class CountUpLiveData : LiveData<Int>() { private

    var count = 0 private val handler = Handler() private val r = Runnable { count++ value = count next() } private fun next() { handler.postDelayed(r, 1000) } override fun onActive() { next() } override fun onInactive() { handler.removeCallbacks(r) } }
  70. 課題7.2 • CountUpLiveData を observe(), observeForever() し、アプリがバックグ ラウンドに⾏ったときのログを確認する • 実装例は次のスライド

  71. 課題7.2 実装例 class MainActivity : AppCompatActivity() { private val liveData

    = CountUpLiveData() private val observer = Observer<Int> { println("observeForever : $it") } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) liveData.observe(this, Observer { println(it) }) liveData.observeForever(observer) } override fun onDestroy() { super.onDestroy() liveData.removeObserver(observer) } }
  72. 課題8.1 • Transformations.map() を使って、CountUpLiveData から来た値の 2乗 をログに出⼒する • 実装例は次のスライド

  73. 課題8.1 実装例 class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState:

    Bundle?) { super.onCreate(savedInstanceState) Transformations .map(CountUpLiveData()) { it * it } .observe(this, Observer { println(it) }) } }
  74. チャレンジ課題

  75. 課題9.1 • CountUpLiveData を source とする MediatorLiveData を作って observe する

    • CountUpLiveData から来た値の2乗値を持つ • CountUpLiveData から来た値が10以上になったら source から外す • 実装例は次のスライド
  76. 課題9.1 実装例 MediatorLiveData<Int>() .apply { val source = CountUpLiveData() addSource(source)

    { if (it < 10) { value = it * it } else { removeSource(source) } } } .observe(this, Observer { println(it) })
  77. 課題10.1 • CountUpLiveData2 を⽤意する class CountUpLiveData2 : LiveData<Int>() { private

    var count = 0 private val handler = Handler() private val r = Runnable { count++ value = count next() } private fun next() { handler.postDelayed(r, 1000) } fun start() { next() } fun stop() { handler.removeCallbacks(r) } }
  78. 課題10.1 • 課題9.1 の source を CountUpLiveData2 に変える • CountUpLiveData2

    を observeForever() し、ログに出⼒する • アプリがバックグラウンドに⾏ったときのログを確認する • addSource で渡す Observer がどのようなときに呼ばれるのか確認する • 実装例は次のスライド
  79. class MainActivity : AppCompatActivity() { private val countUpLiveData2 = CountUpLiveData2()

    private val observer = Observer<Int> { println("observeForever : $it") } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) countUpLiveData2.observeForever(observer) MediatorLiveData<Int>() .apply { addSource(countUpLiveData2) { if (it < 10) { value = it * it } else { removeSource(countUpLiveData2) } } } .observe(this, Observer { println(it) }) countUpLiveData2.start() } override fun onDestroy() { super.onDestroy() countUpLiveData2.stop() countUpLiveData2.removeObserver(observer) } }
  80. ViewModel

  81. 課題11.1 • MainViewModel を⽤意する class MainViewModel : ViewModel() { }

  82. 課題11.2 • この場合 viewModel1 と viewModel2 は同じインスタンスになるか確認する • 画⾯回転した場合、回転後と回転前で同じインスタンスになるか確認する class

    MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val viewModel1 = ViewModelProviders.of(this).get(MainViewModel::class.java) val viewModel2 = ViewModelProviders.of(this).get(MainViewModel::class.java) } }
  83. 課題12.1 • MainActivity に MainFragment を追加する class MainActivity : AppCompatActivity()

    { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .add(MainFragment(), "MainFragment") .commit() } val viewModel1 = ViewModelProviders.of(this).get(MainViewModel::class.java) } }
  84. 課題12.2 • この場合 viewModel1, viewModel2, viewModel3 は同じインスタンスになるか確認する • 画⾯回転した場合、回転後と回転前で同じインスタンスになるか確認する class

    MainFragment : Fragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) val viewModel2 = ViewModelProviders.of(this).get(MainViewModel::class.java) val viewModel3 = ViewModelProviders.of(requireActivity()) .get(MainViewModel::class.java) } }
  85. 課題13 class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?)

    { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val viewModel1 = ViewModelProviders.of(this).get("1", MainViewModel::class.java) val viewModel2 = ViewModelProviders.of(this).get("2", MainViewModel::class.java) } } • この場合 viewModel1, viewModel2 は同じインスタンスになるか確認する • 画⾯回転した場合、回転後と回転前で同じインスタンスになるか確認する
  86. 課題13.2 • MainActivity に MainFragment2 を 2つ 追加する class MainActivity

    : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .add(MainFragment2.newInstance(1), "MainFragment21") .add(MainFragment2.newInstance(2), "MainFragment22") .commit() } val viewModel1 = ViewModelProviders.of(this).get("1", MainViewModel::class.java) val viewModel2 = ViewModelProviders.of(this).get("2", MainViewModel::class.java) } }
  87. • この場合 viewModel1, viewModel2, viewModel3 の関係がどうなるか確認する • 画⾯回転した場合、回転後と回転前で同じインスタンスになるか確認する class MainFragment2

    : Fragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) val index = arguments!!.getInt("index") val viewModel3 = ViewModelProviders.of(requireActivity()) .get(index.toString(), MainViewModel::class.java) } companion object { fun newInstance(index: Int) = MainFragment2().apply { arguments = bundleOf("index" to index) } } }
  88. 課題14.1 • 課題7.1 の CountUpLiveData を ViewModel で保持し、Activity から observe

    する • 画⾯回転したときに Observer に渡される値がどうなるか確認する
  89. class MainViewModel : ViewModel() { val countUpLiveData = CountUpLiveData() }

    class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val viewModel: MainViewModel = ViewModelProviders .of(this) .get(MainViewModel::class.java) viewModel.countUpLiveData.observe(this, Observer { println(it) }) } }
  90. 課題15 class MainActivity : AppCompatActivity() { private val liveData =

    MutableLiveData<Int>().apply { value = 0 } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) button.setOnClickListener { val count = liveData.value = count } liveData.observe(this, Observer { println("count = $it") }) } } • ViewModel を導⼊して、画⾯回転しても count が 0 に戻らないようにする
  91. class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java) button.setOnClickListener { viewModel.countUp() } viewModel.liveData.observe(this, Observer { println("count = $it") }) } } class MainViewModel : ViewModel() { private val _liveData = MutableLiveData<Int>().apply { value = 0 } val liveData: LiveData<Int> get() = _liveData fun countUp() { val count = (_liveData.value ?: 0) + 1 _liveData.value = count } }
  92. チャレンジ課題

  93. 課題16 • 以下の仕様を満たすように次スライドの RandomLoadLiveData を完成させ る • onActive() が呼ばれたとき値が null

    または Status.Error の場合 load() を ⾏う • reload() が呼ばれたとき Load 中じゃなければ load() を⾏う • load() が呼ばれたら⼀秒後に Random.nextBoolean() を呼び、true の場 合は Status.Success(System.currentTimeMillis())を、false の場合 Status.Error を値にセットする
  94. sealed class Status<out T> { object Loading : Status<Nothing>() data

    class Success<out T>(val value: T) : Status<T>() data class Error(val throwable: Throwable) : Status<Nothing>() } class RandomLoadLiveData : LiveData<Status<Long>>() { // TODO } class MainViewModel : ViewModel() { // TODO }
  95. class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java) button.setOnClickListener { viewModel.reload() } viewModel.liveData.observe(this, Observer { when(it) { Status.Loading -> { progressBar.visibility = View.VISIBLE button.visibility = View.GONE } is Status.Success -> { progressBar.visibility = View.GONE button.visibility = View.VISIBLE button.text = it.value.toString() } is Status.Error -> { progressBar.visibility = View.GONE button.visibility = View.VISIBLE button.text = it.throwable.message } } }) } }
  96. 実装例

  97. class RandomLoadLiveData : LiveData<Status<Long>>() { private val handler = Handler()

    private val r = Runnable { value = if (Random.nextBoolean()) { Status.Success(System.currentTimeMillis()) } else { Status.Error(IllegalStateException("failed")) } } override fun onActive() { if (value == null || value is Status.Error) { load() } } fun reload() { if (value !is Status.Loading) { load() } } private fun load() { value = Status.Loading handler.postDelayed(r, 1000) } fun cancel() { handler.removeCallbacks(r) } } class MainViewModel : ViewModel() { private val _liveData = RandomLoadLiveData() val liveData: LiveData<Status<Long>> get() = _liveData fun reload() { _liveData.reload() } override fun onCleared() { super.onCleared() _liveData.cancel() } }