$30 off During Our Annual Pro Sale. View Details »

Lifecycle, ViewModel, LiveData の復習

Lifecycle, ViewModel, LiveData の復習

Android Architecture Components 勉強会

Yuki Anzai

March 25, 2019
Tweet

More Decks by Yuki Anzai

Other Decks in Technology

Transcript

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

    View Slide

  2. Architecture Components 勉強会 とは
    • Architecture Components の基礎知識について学ぶ勉強会
    • 主催:GDG Tokyo ハッシュタグ: #gdgtokyo
    • 講師、チューター:Google 社員と Google Developer Expert
    • (あんざいゆき、 えがわ、わさびーふ、あらき)
    • 当⽇チューター : @itome, @takahirom, @mokelab, @zaki50

    View Slide

  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~ 課題取り組み

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  9. Architecture Components の⽬的
    堅牢 (robust) で
    テストしやすく (testable)
    保守しやすい (maintainable)
    アプリの設計ができるように⼿助けする

    View Slide

  10. 復習

    View Slide

  11. Lifecycles

    View Slide

  12. Lifecycles
    • Lifecycles = Lifecycle-aware Components
    • ライフサイクルを感知するコンポーネント
    • Activity や Fragment のライフサイクル状態が変わったときに何
    かアクションを起こす、ということができるようになる

    View Slide

  13. Lifecycle.State
    • 現在のライフサイクル状態を表す enum
    • INITIALIZED
    • DESTROYED
    • CREATED
    • STARTED
    • RESUMED
    https://developer.android.com/topic/libraries/architecture/lifecycle.html

    View Slide

  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

    View Slide

  15. Lifecycle
    val state = lifecycle.currentState
    現在のライフサイクル状態を取得する
    val isAtLeastResumed = lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)
    現在のライフサイクル状態が xx より後であるかどうか

    View Slide

  16. LifecycleOwner
    • クラスが Lifecycle を持つことを表す single method interface
    • FragmentActivity の親クラスである ComponentActivity が
    LifecycleOwner を実装している
    • Fragment は LifecycleOwner を実装している
    public interface LifecycleOwner {
    @NonNull
    Lifecycle getLifecycle();
    }

    View Slide

  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
    }
    }

    View Slide

  18. LifecycleObserver
    • Lifecycle に追加・削除できるオブザーバーを表す interface
    • メソッドに @OnLifecycleEvent アノテーションをつけると、対応する
    Lifecycle.Event が発⾏されたときにメソッドが呼ばれる
    public interface LifecycleObserver {
    }

    View Slide

  19. OnLifecycleEvent
    • LifecycleObserver のメソッドにつけるアノテーション
    • LifecycleEvent を指定する
    val observer: LifecycleObserver = object : LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onStart() {
    }
    }

    View Slide

  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) {
    }
    }

    View Slide

  21. LifecycleService
    • LifecycleOwner が実装された Service
    class MyService : LifecycleService() {
    override fun onCreate() {
    super.onCreate()
    val lifecycle = lifecycle
    }
    }

    View Slide

  22. ProcessLifecycleOwner
    • アプリのプロセス全体に対する Lifecycle を提供する
    • ON_CREATE は⼀度だけ発⾏される
    • ON_DESTROY は発⾏されない
    ProcessLifecycleOwner.get()
    .lifecycle
    .addObserver(observer)

    View Slide

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

    View Slide

  24. LiveData

    View Slide

  25. 主な LiveData
    • LiveData
    • 値の変更を Observe できるデータホルダー
    • MutableLiveData
    • 外部から変更可能な LiveData
    • MediatorLiveData
    • 複数の LiveData を束ねて管理する MutableLiveData

    View Slide

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

    View Slide

  27. 値の変更を Observe する
    • 値が変更された → すぐに通知される
    @MainThread
    public void observeForever(@NonNull Observer super T> observer)

    View Slide

  28. 現在の値を取得する
    • ⼀度も値がセットされていないときは null が返る
    public T getValue()

    View Slide

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

    View Slide

  30. Observer
    • LiveData からの値を受け取るシンプルなインタフェース
    • LiveData から値が通知される = onChanged() が呼ばれる
    public interface Observer {
    void onChanged(T t);
    }

    View Slide

  31. MutableLiveData
    • LiveData では値を変更するメソッドは protected
    • MutableLiveData では値を変更するメソッドが public になっている
    public void setValue(T value)
    public void postValue(T value)
    Main スレッド外から値を設定する際の便利メソッド
    値を設定する(Main スレッドで呼び出すこと)

    View Slide

  32. MediatorLiveData
    • 複数の LiveData を束ねて管理できる
    • 型の異なる複数の LiveData にイベントを伝播させることができる
    @MainThread
    public void addSource(@NonNull LiveData source,
    @NonNull Observer super S> onChanged)
    @MainThread
    public void removeSource(@NonNull LiveData toRemote)

    View Slide

  33. Transformations
    • MediatorLiveData を使いやすくしたユーティリティ
    @MainThread
    public static LiveData map(
    @NonNull LiveData source,
    @NonNull final Function mapFunction)
    @MainThread
    public static LiveData switchMap(
    @NonNull LiveData source,
    @NonNull final Function> switchMapFunction)

    View Slide

  34. ViewModel

    View Slide

  35. ViewModel
    • Activity の画⾯回転時のデータ保持
    • Activity の複数 Fragment 間でのデータ受け渡し
    • LiveData と併⽤することが多い
    • プロセス停⽌後は復旧できない
    • データの永続化ではない

    View Slide

  36. ViewModel

    View Slide

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

    View Slide

  38. ViewModel の定義
    • ViewModel または AndroidViewModel を継承する
    class MyViewModel : ViewModel() {
    }
    class MyViewModel(private val app: Application) : AndroidViewModel(app) {
    }

    View Slide

  39. リソースの解放
    • onCleared() で不要になったリソースを解放してリークを防⽌する
    class MainViewModel : ViewModel() {
    override fun onCleared() {
    super.onCleared()
    // ここでリソースを解放する
    }
    }

    View Slide

  40. ViewModel のインスタンス化
    val viewModel: T = ViewModelProviders
    .of(, factory)
    .get(::class.java)
    • ViewModelProviders.of() と ViewModelProvider.get() を使う
    • ⾃分で new しない!
    val viewModel: MainViewModel = ViewModelProviders
    .of(this)
    .get(MainViewModel::class.java)

    View Slide

  41. ViewModelProvider.Factory
    • ViewModelProviders.of() の第2引数に指定する
    public interface Factory {
    @NonNull
    T create(@NonNull Class modelClass);
    }
    val factory: ViewModelProvider.Factory = ...
    val viewModel: MainViewModel = ViewModelProviders
    .of(this, factory)
    .get(MainViewModel::class.java)

    View Slide

  42. What's new in 2.1.0-alpha

    View Slide

  43. LifecycleEventObserver
    • 全ての lifecycle change を受け取れる interface
    https://developer.android.com/reference/androidx/lifecycle/LifecycleEventObserver
    lifecycle.addObserver(LifecycleEventObserver { source, event ->
    println("${source.lifecycle.currentState} : $event")
    })

    View Slide

  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)

    View Slide

  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) {
    ...
    }

    View Slide

  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"

    View Slide

  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("time")
    val liveData: LiveData
    get() = _liveData
    fun update() {
    _liveData.value = System.currentTimeMillis()
    }
    }

    View Slide

  48. 課題

    View Slide

  49. Lifecycles

    View Slide

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

    View Slide

  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

    View Slide

  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}")
    }
    }

    View Slide

  53. View Slide

  54. 課題 2.1
    • LifecycleObserver の実装を⽤意して Lifecycle に登録する
    val lifecycleObserver = object : LifecycleObserver {
    }
    lifecycle.addObserver(lifecycleObserver)

    View Slide

  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}")
    }
    }

    View Slide

  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}")
    }
    }

    View Slide

  57. 課題 4.0
    • Application を継承した MainApplication を⽤意する
    • MainApplication を AndroidManifest.xml に設定する
    class MainApplication : Application() {
    }
    android:name=".MainApplication"
    ...>
    ...

    View Slide

  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)
    }
    }

    View Slide

  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")
    }
    }

    View Slide

  60. 課題 4.3
    • いろんな操作をしたときのログを確認する
    • ホームキーをタップしたとき
    • Recent Apps から復帰したとき
    • 別の Activity に遷移したとき
    • 別の Activity から戻ってきたとき
    • 画⾯回転したとき

    View Slide

  61. • バックキーでアプリを終了したあと、ランチャーや RecentApps から起動
    したとき ProcessLifecycleOwner で ON_CREATE は呼ばれる?
    • Recent Apps からクリアーしたあと、ランチャーから起動したとき
    ProcessLifecycleOwner で ON_CREATE は呼ばれる?

    View Slide

  62. LiveData

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  66. 課題6.1
    • LiveData を継承した MainLiveData を⽤意し、onActive() と onInactive()
    で hasActiveObservers() の結果を確認する
    class MainLiveData : LiveData() {
    override fun onActive() {
    super.onActive()
    println("onActive : ${hasActiveObservers()}")
    }
    override fun onInactive() {
    super.onInactive()
    println("onInactive : ${hasActiveObservers()}")
    }
    }

    View Slide

  67. 課題6.2
    • MainActivity の onCreate() で MainLiveData を⽣成する
    • MainLiveData を observe し、いろんな操作をしたときのログを確認する
    • ホームキーをタップしたとき
    • Recent Apps から復帰したとき
    • 別の Activity に遷移したとき
    • 別の Activity から戻ってきたとき
    • 画⾯回転したとき

    View Slide

  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)
    }
    }

    View Slide

  69. 課題7.1
    • CountUpLiveData を⽤意する
    class CountUpLiveData : LiveData() {
    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) }
    }

    View Slide

  70. 課題7.2
    • CountUpLiveData を observe(), observeForever() し、アプリがバックグ
    ラウンドに⾏ったときのログを確認する
    • 実装例は次のスライド

    View Slide

  71. 課題7.2 実装例
    class MainActivity : AppCompatActivity() {
    private val liveData = CountUpLiveData()
    private val observer = Observer { 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)
    }
    }

    View Slide

  72. 課題8.1
    • Transformations.map() を使って、CountUpLiveData から来た値の 2乗
    をログに出⼒する
    • 実装例は次のスライド

    View Slide

  73. 課題8.1 実装例
    class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    Transformations
    .map(CountUpLiveData()) { it * it }
    .observe(this, Observer {
    println(it)
    })
    }
    }

    View Slide

  74. チャレンジ課題

    View Slide

  75. 課題9.1
    • CountUpLiveData を source とする MediatorLiveData を作って observe
    する
    • CountUpLiveData から来た値の2乗値を持つ
    • CountUpLiveData から来た値が10以上になったら source から外す
    • 実装例は次のスライド

    View Slide

  76. 課題9.1 実装例
    MediatorLiveData()
    .apply {
    val source = CountUpLiveData()
    addSource(source) {
    if (it < 10) {
    value = it * it
    } else {
    removeSource(source)
    }
    }
    }
    .observe(this, Observer {
    println(it)
    })

    View Slide

  77. 課題10.1
    • CountUpLiveData2 を⽤意する
    class CountUpLiveData2 : LiveData() {
    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) }
    }

    View Slide

  78. 課題10.1
    • 課題9.1 の source を CountUpLiveData2 に変える
    • CountUpLiveData2 を observeForever() し、ログに出⼒する
    • アプリがバックグラウンドに⾏ったときのログを確認する
    • addSource で渡す Observer がどのようなときに呼ばれるのか確認する
    • 実装例は次のスライド

    View Slide

  79. class MainActivity : AppCompatActivity() {
    private val countUpLiveData2 = CountUpLiveData2()
    private val observer = Observer { println("observeForever : $it") }
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    countUpLiveData2.observeForever(observer)
    MediatorLiveData()
    .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)
    }
    }

    View Slide

  80. ViewModel

    View Slide

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

    View Slide

  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)
    }
    }

    View Slide

  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)
    }
    }

    View Slide

  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)
    }
    }

    View Slide

  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 は同じインスタンスになるか確認する
    • 画⾯回転した場合、回転後と回転前で同じインスタンスになるか確認する

    View Slide

  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)
    }
    }

    View Slide

  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)
    }
    }
    }

    View Slide

  88. 課題14.1
    • 課題7.1 の CountUpLiveData を ViewModel で保持し、Activity から
    observe する
    • 画⾯回転したときに Observer に渡される値がどうなるか確認する

    View Slide

  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)
    })
    }
    }

    View Slide

  90. 課題15
    class MainActivity : AppCompatActivity() {
    private val liveData = MutableLiveData().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 に戻らないようにする

    View Slide

  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().apply { value = 0 }
    val liveData: LiveData
    get() = _liveData
    fun countUp() {
    val count = (_liveData.value ?: 0) + 1
    _liveData.value = count
    }
    }

    View Slide

  92. チャレンジ課題

    View Slide

  93. 課題16
    • 以下の仕様を満たすように次スライドの RandomLoadLiveData を完成させ

    • onActive() が呼ばれたとき値が null または Status.Error の場合 load() を
    ⾏う
    • reload() が呼ばれたとき Load 中じゃなければ load() を⾏う
    • load() が呼ばれたら⼀秒後に Random.nextBoolean() を呼び、true の場
    合は Status.Success(System.currentTimeMillis())を、false の場合
    Status.Error を値にセットする

    View Slide

  94. sealed class Status {
    object Loading : Status()
    data class Success(val value: T) : Status()
    data class Error(val throwable: Throwable) : Status()
    }
    class RandomLoadLiveData : LiveData>() {
    // TODO
    }
    class MainViewModel : ViewModel() {
    // TODO
    }

    View Slide

  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
    }
    }
    })
    }
    }

    View Slide

  96. 実装例

    View Slide

  97. class RandomLoadLiveData : LiveData>() {
    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>
    get() = _liveData
    fun reload() {
    _liveData.reload()
    }
    override fun onCleared() {
    super.onCleared()
    _liveData.cancel()
    }
    }

    View Slide