5月26日,星期二。今天是第#414期Android Weekly的学习笔记。

ViewModel and SavedStatehandle: always retain state

https://www.rockandnull.com/viewmodel-savedstate/

一篇关于ViewModel数据恢复的小文。

进程清理和数据恢复

我们知道ViewModel可以解决诸如Orientation切换时,Activity/Fragment销毁重建的数据保存问题。然而,有时甚至连ViewModel自身也会被销毁,这种情况发生在应用位于后台且内存不足的情况下,系统会杀死进程。杀死进程的顺序是Least Recently Used (LRU)

如果对此什么都不做的话,默认行为是重启App,如果想要在这个过程中重新恢复到App被清理前的页面,就必须通过类似onSaveInstanceState的方法来保存数据。

对此,Jetpack提供了ViewModel’s Saved State module组件,通过它,可以方便地恢复ViewModel状态。

ViewModel-SaveState 的使用

通过gradle接入,在这里查看最新版本。

1
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"

不包含构造参数的ViewModel

在构造器里增加一个SavedStateHandle类型的参数。

1
2
3
class MyViewModel(private val state: SavedStateHandle) : ViewModel() {
// some other code
}

然后通过代理方式重写model

1
override val model by viewModels<MyVioewModel>

包含构造参数的ViewModel

对于包含参数的ViewModel(通过ViewModelFactory初始化),需要继承AbstractSavedStateViweModelFactory类以实现SaveState功能。

1
2
3
4
5
6
7
8
9
10
11
class MyViewModelFactory(owner: SavedStateRegistryOwner,
private val myId: Int,
defaultArgs: Bundle? = null
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {

override fun <T : ViewModel?> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T = MyViewModel(handle, myId) as T
}

然后在ViewModel里进行代理:

1
2
3
override val model by viewModels<MyViewModel> {
MyViewModelFactory(this, args.myId)
}

结合LiveData使用

通过Key-Value方式获取LiveData对象,而非直接构建。同一个Key往往会返回同一个Value,因为是持久化保存数据,Item类必须实现Parcellable接口,或者更简便地,通过@Parcelize注解,关于Parcelize可以阅读KEEP的这篇文章

1
2
3
4
5
6
class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {

private val itemsLiveData = savedStateHandle.getLiveData<Item>("itemsKey")

// some other code
}

模拟低内存场景

  1. 启动你的APP
  2. 切换到后台
  3. 执行adb shell am kill your.package.name
  4. 再次启动APP

注意SavedSateHandle只能处理ViewModel中的数据,对于全局的静态单例,仍然需要通过人工的方式处理。

The Android Lifecycle cheat sheet — part IV : ViewModels, Translucent Activities and Launch Modes

https://medium.com/androiddevelopers/the-android-lifecycle-cheat-sheet-part-iv-49946659b094

Android Lifecycle cheat sheet 是Jose Alcerreca写的一系列生命周期总结文章,通过简单直观的图片表达。

第4期主要分析ViewModel、透明Activity和不同启动模式下的生命周期。

ViewModels

应用于Activity和Fragment,在onCreate结束时初始化,在其销毁时销毁自身。

lifecycle_viewmodel.png
lifecycle_viewmodel.png

透明Activity

通过android:windowIsTranslucent属性将Activity设定为透明,当在一个透明Activity上启动新的Activity时,原透明Activity只会执行onPause,并不会onStop,并且在他Pause的时候,还可以接受UI事件。

lifecycle_translucent.png
lifecycle_translucent.png

当按下Home键时,位于底层的透明Activity会进入onStop状态,并且当用户切换回应用后,依次执行onRestartonStart

lifecycle_translucent_home.png
lifecycle_translucent_home.png

Launch Modes

关于启动模式的建议是——只使用默认的standard启动模式。更多细节可以参阅这篇文章:Tasks and Back Stack

这是SINGLE_TOP模式下的生命周期&栈情况。

lifecycle_single_top.png
lifecycle_single_top.png

然后是SINGLE_TASK,再次强调,强烈不建议使用这种启动模式。

lifecycle_single_task.png
lifecycle_single_task.png

Understanding Kotlin Coroutines with this mental model

https://www.lukaslechner.com/understanding-kotlin-coroutines-with-this-mental-model/

什么是思维模型(Mental Model)

  • 思维模型是我们理解世界的方式
  • 我们通过思维模型将负责的事情简化
  • 思维模型藐视事物如何运行

简单说,思维模型让我们知道How it works。

Routines

routine.png
routine.png

CoroutineCOROUTINE两部分构成,Routine的概念是,一旦启动,就会执行完。而CO则为其赋予了并行的含义。

routine的运行方式如下,1和2顺序执行。

routine.png
routine.png

Coroutines

在调用后会立即返回,1和2的完成先后顺序不固定。

coroutines.png
coroutines.png

Coroutine内部调用suspend函数的地方被称为“suspension point”,即中断点。

suspension_point.png
suspension_point.png

Coroutine思维模型要点

  1. Coroutine可以在调用后立即返回,在中断点中断后,过一段时间恢复
  2. 你可以通过Coroutine在不切换线程的情况下,执行并发任务,从而达到更高的运行效率
  3. Coroutine是线程之上的抽象,一个Coroutine内部的代码也可以运行在不同线程上
  4. 编译器将suspend函数转换为常规函数,同时增加一个Cotinuation类型的参数,这是一种状态机
  5. delay()函数是非阻塞的,它使用类似handler.postDelayed()的方式实现

Kotlin withContext vs Async-await

https://blog.mindorks.com/kotlin-withcontext-vs-async-await

最后一篇依然是关于Coroutine,作者介绍了两种启动协程的方式withContextasync-await的应用场景,守则如下。

  • 它们都可以用来获取结果
  • 不需要并行执行时,用withContext
  • 当且仅当需要并行时,用async