Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

復習

Slide 11

Slide 11 text

Lifecycles

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

LiveData

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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)

Slide 34

Slide 34 text

ViewModel

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

ViewModel

Slide 37

Slide 37 text

liveData.observe(lifecycleOwner, observer) ViewModel + LiveData

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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)

Slide 41

Slide 41 text

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)

Slide 42

Slide 42 text

What's new in 2.1.0-alpha

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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)

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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"

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

課題

Slide 49

Slide 49 text

Lifecycles

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

課題 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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

課題 4.1 • MainApplication の onCreate() で LifecycleObserver の実装を⽤意し、 ProcessLifecycleOwner の Lifecycle に登録する class MainApplication : Application() { override fun onCreate() { super.onCreate() val lifecycleObserver = object : LifecycleObserver { } ProcessLifecycleOwner.get() .lifecycle .addObserver(lifecycleObserver) } }

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

LiveData

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

課題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()}") } }

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

チャレンジ課題

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

ViewModel

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

• この場合 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) } } }

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

課題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 に戻らないようにする

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

チャレンジ課題

Slide 93

Slide 93 text

課題16 • 以下の仕様を満たすように次スライドの RandomLoadLiveData を完成させ る • onActive() が呼ばれたとき値が null または Status.Error の場合 load() を ⾏う • reload() が呼ばれたとき Load 中じゃなければ load() を⾏う • load() が呼ばれたら⼀秒後に Random.nextBoolean() を呼び、true の場 合は Status.Success(System.currentTimeMillis())を、false の場合 Status.Error を値にセットする

Slide 94

Slide 94 text

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 }

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

実装例

Slide 97

Slide 97 text

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