Share
Embed
Architecture Components 勉強会 第5回目 Lifecycle, ViewModel, LiveData の復習
Android Architecture Components 勉強会 #5
https://gdg-tokyo.connpass.com/event/123068/
Yuki Anzai
March 25, 2019
Transcript
97 slides
-
Architecture Components 勉強会 とは • Architecture Components の基礎知識について学ぶ勉強会 • 主催:GDG
Tokyo ハッシュタグ: #gdgtokyo • 講師、チューター:Google 社員と Google Developer Expert • (あんざいゆき、 えがわ、わさびーふ、あらき) • 当⽇チューター : @itome, @takahirom, @mokelab, @zaki50 -
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~ 課題取り組み -
Architecture Components とは 複数の機能・ライブラリの総称 • Data Binding • Lifecycles •
LiveData • ViewModel • Room • Paging • WorkManager • Navigation -
Architecture Components とは 複数の機能・ライブラリの総称 • Data Binding • Lifecycles •
LiveData • ViewModel • Room • Paging • WorkManager • Navigation 個別利⽤OK 組み合わせ利⽤OK -
Architecture Components とは 複数の機能・ライブラリの総称 • Data Binding • Lifecycles •
LiveData • ViewModel • Room • Paging • WorkManager • Navigation 個別利⽤OK 組み合わせ利⽤OK #1 #2 #3 #4 -
Architecture Components とは 複数の機能・ライブラリの総称 • Data Binding • Lifecycles •
LiveData • ViewModel • Room • Paging • WorkManager • Navigation 個別利⽤OK 組み合わせ利⽤OK #1 #2 #3 #4 今⽇は これの 復習 -
Architecture Components とは 複数の機能・ライブラリの総称 • Data Binding • Lifecycles •
LiveData • ViewModel • Room • Paging • WorkManager • Navigation 個別利⽤OK 組み合わせ利⽤OK #1 #2 #3 #4 今⽇は これの 復習 #6 #7 #8 -
Lifecycles • Lifecycles = Lifecycle-aware Components • ライフサイクルを感知するコンポーネント • Activity
や Fragment のライフサイクル状態が変わったときに何 かアクションを起こす、ということができるようになる -
Lifecycle.State • 現在のライフサイクル状態を表す enum • INITIALIZED • DESTROYED • CREATED
• STARTED • RESUMED https://developer.android.com/topic/libraries/architecture/lifecycle.html -
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 -
LifecycleOwner • クラスが Lifecycle を持つことを表す single method interface • FragmentActivity
の親クラスである ComponentActivity が LifecycleOwner を実装している • Fragment は LifecycleOwner を実装している public interface LifecycleOwner { @NonNull Lifecycle getLifecycle(); } -
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 } } -
LifecycleObserver • Lifecycle に追加・削除できるオブザーバーを表す interface • メソッドに @OnLifecycleEvent アノテーションをつけると、対応する Lifecycle.Event
が発⾏されたときにメソッドが呼ばれる public interface LifecycleObserver { } -
OnLifecycleEvent • LifecycleObserver のメソッドにつけるアノテーション • LifecycleEvent を指定する val observer: LifecycleObserver
= object : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onStart() { } } -
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) { } } -
LifecycleService • LifecycleOwner が実装された Service class MyService : LifecycleService() {
override fun onCreate() { super.onCreate() val lifecycle = lifecycle } } -
ProcessLifecycleOwner • アプリのプロセス全体に対する Lifecycle を提供する • ON_CREATE は⼀度だけ発⾏される • ON_DESTROY
は発⾏されない ProcessLifecycleOwner.get() .lifecycle .addObserver(observer) -
ProcessLifecycleOwner • ON_START, ON_RESUME は、最初の Activity がそのイベントを通るとき に発⾏される • ON_STOP,
ON_PAUSE は、最後の Activity がそのイベントを通るときに遅 延を伴って発⾏される • コンフィギュレーションチェンジによる再⽣成時は発⾏されない * 遅延は destruction 時のみ -
主な LiveData • LiveData • 値の変更を Observe できるデータホルダー • MutableLiveData
• 外部から変更可能な LiveData • MediatorLiveData • 複数の LiveData を束ねて管理する MutableLiveData -
値の変更を Observe する • LifecycleOwner の Lifecycle が • アクティブのときに値が変更された
→ すぐに通知される • アクティブじゃないときに値が変更された → 次にアクティブになった ときに通知される @MainThread public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) -
値の変更を Observe する • 値が変更された → すぐに通知される @MainThread public void
observeForever(@NonNull Observer<? super T> observer) -
active / inactive • アクティブな Observer の数が1以上になったときに呼ばれる protected void onActive()
protected void onInactive() • アクティブな Observer の数が1未満になったときに呼ばれる -
Observer • LiveData からの値を受け取るシンプルなインタフェース • LiveData から値が通知される = onChanged() が呼ばれる
public interface Observer<T> { void onChanged(T t); } -
MutableLiveData • LiveData では値を変更するメソッドは protected • MutableLiveData では値を変更するメソッドが public になっている
public void setValue(T value) public void postValue(T value) Main スレッド外から値を設定する際の便利メソッド 値を設定する(Main スレッドで呼び出すこと) -
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) -
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) -
ViewModel • Activity の画⾯回転時のデータ保持 • Activity の複数 Fragment 間でのデータ受け渡し •
LiveData と併⽤することが多い • プロセス停⽌後は復旧できない • データの永続化ではない -
ViewModel の定義 • ViewModel または AndroidViewModel を継承する class MyViewModel :
ViewModel() { } class MyViewModel(private val app: Application) : AndroidViewModel(app) { } -
リソースの解放 • onCleared() で不要になったリソースを解放してリークを防⽌する class MainViewModel : ViewModel() { override
fun onCleared() { super.onCleared() // ここでリソースを解放する } } -
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) -
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) -
LifecycleEventObserver • 全ての lifecycle change を受け取れる interface https://developer.android.com/reference/androidx/lifecycle/LifecycleEventObserver lifecycle.addObserver(LifecycleEventObserver {
source, event -> println("${source.lifecycle.currentState} : $event") }) -
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) -
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) { ... } -
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" -
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() } } -
課題 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 -
課題 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}") } } -
課題 2.1 • LifecycleObserver の実装を⽤意して Lifecycle に登録する val lifecycleObserver =
object : LifecycleObserver { } lifecycle.addObserver(lifecycleObserver) -
課題 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}") } } -
課題 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}") } } -
課題 4.0 • Application を継承した MainApplication を⽤意する • MainApplication を
AndroidManifest.xml に設定する class MainApplication : Application() { } <application android:name=".MainApplication" ...> ... </application> -
課題 4.1 • MainApplication の onCreate() で LifecycleObserver の実装を⽤意し、 ProcessLifecycleOwner
の Lifecycle に登録する class MainApplication : Application() { override fun onCreate() { super.onCreate() val lifecycleObserver = object : LifecycleObserver { } ProcessLifecycleOwner.get() .lifecycle .addObserver(lifecycleObserver) } } -
課題 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") } } -
課題 4.3 • いろんな操作をしたときのログを確認する • ホームキーをタップしたとき • Recent Apps から復帰したとき
• 別の Activity に遷移したとき • 別の Activity から戻ってきたとき • 画⾯回転したとき -
• バックキーでアプリを終了したあと、ランチャーや RecentApps から起動 したとき ProcessLifecycleOwner で ON_CREATE は呼ばれる? •
Recent Apps からクリアーしたあと、ランチャーから起動したとき ProcessLifecycleOwner で ON_CREATE は呼ばれる? -
課題 5.1 • ボタンをタップしたとき、どのような順番で Observer に通知されるか確 認する val liveData =
MutableLiveData<String>() liveData.observe(this, Observer { println(it) }) button.setOnClickListener { liveData.value = "a" liveData.value = "b" } -
課題 5.2 • ボタンをタップしたとき、どのような順番で Observer に通知されるか確 認する val liveData =
MutableLiveData<String>() liveData.observe(this, Observer { println(it) }) button.setOnClickListener { liveData.postValue("a") liveData.value = "b" } -
課題 5.3 • ボタンをタップしたとき、どのような順番で Observer に通知されるか確 認する val liveData =
MutableLiveData<String>() liveData.observe(this, Observer { println(it) }) button.setOnClickListener { liveData.postValue("a") liveData.postValue("b") } -
課題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()}") } } -
課題6.2 • MainActivity の onCreate() で MainLiveData を⽣成する • MainLiveData
を observe し、いろんな操作をしたときのログを確認する • ホームキーをタップしたとき • Recent Apps から復帰したとき • 別の Activity に遷移したとき • 別の Activity から戻ってきたとき • 画⾯回転したとき -
課題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) } } -
課題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) } } -
課題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) } } -
課題8.1 実装例 class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState:
Bundle?) { super.onCreate(savedInstanceState) Transformations .map(CountUpLiveData()) { it * it } .observe(this, Observer { println(it) }) } } -
課題9.1 • CountUpLiveData を source とする MediatorLiveData を作って observe する
• CountUpLiveData から来た値の2乗値を持つ • CountUpLiveData から来た値が10以上になったら source から外す • 実装例は次のスライド -
課題9.1 実装例 MediatorLiveData<Int>() .apply { val source = CountUpLiveData() addSource(source)
{ if (it < 10) { value = it * it } else { removeSource(source) } } } .observe(this, Observer { println(it) }) -
課題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) } } -
課題10.1 • 課題9.1 の source を CountUpLiveData2 に変える • CountUpLiveData2
を observeForever() し、ログに出⼒する • アプリがバックグラウンドに⾏ったときのログを確認する • addSource で渡す Observer がどのようなときに呼ばれるのか確認する • 実装例は次のスライド -
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) } } -
課題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) } } -
課題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) } } -
課題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) } } -
課題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 は同じインスタンスになるか確認する • 画⾯回転した場合、回転後と回転前で同じインスタンスになるか確認する -
課題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) } } -
• この場合 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) } } } -
課題14.1 • 課題7.1 の CountUpLiveData を ViewModel で保持し、Activity から observe
する • 画⾯回転したときに Observer に渡される値がどうなるか確認する -
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) }) } } -
課題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 に戻らないようにする -
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 } } -
課題16 • 以下の仕様を満たすように次スライドの RandomLoadLiveData を完成させ る • onActive() が呼ばれたとき値が null
または Status.Error の場合 load() を ⾏う • reload() が呼ばれたとき Load 中じゃなければ load() を⾏う • load() が呼ばれたら⼀秒後に Random.nextBoolean() を呼び、true の場 合は Status.Success(System.currentTimeMillis())を、false の場合 Status.Error を値にセットする -
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 } -
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 } } }) } } -
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() } }