如何在 Android 中保存和恢复 lambdas?

Rog*_*Oba 4 android kotlin state-restoration

在Android中实现状态恢复时,如何保存和恢复一个lambda?

我尝试将其保存为 Serializable 和 Parcelable,但它引发了编译错误。

有什么方法可以保存和恢复它们,还是应该寻求其他方法?

Rog*_*Oba 6

Kotlin lambdas 实现Serializable,所以它们不能像这样保存:

override fun onSaveInstanceState(outState: Bundle) {
    outState.putSerializable("YOUR_TAG", myLambda as Serializable)
    super.onSaveInstanceState(outState)
}
Run Code Online (Sandbox Code Playgroud)

同样,要恢复它们:

override fun onCreate(savedInstanceState: Bundle?) {
    myLambda = savedInstanceState?.getSerializable("YOUR_TAG") as (MyObject) -> Void
    super.onCreate(savedInstanceState)
}
Run Code Online (Sandbox Code Playgroud)

(这显然可以在为您提供 的任何生命周期事件中完成savedInstanceState,因为这只是一个示例)

一些注意事项:

  • 保存它们时,它们需要被强制转换,否则编译器会抱怨(出于某种原因)。
  • import java.io.Serializable 是必须的。
  • 您将其转换回 lambda 类型的方法将引发警告Unchecked cast: Serializable? to YourLambdaType。此强制转换是安全的(假设您正确推断可空性!),因此您可以通过使用安全地抑制此警告@Suppress("UNCHECKED_CAST")
  • MyObject必须是Serializableor Parcelable,否则它会在运行时崩溃。

现在有一个细节没有告诉任何地方并且在运行时崩溃,没有有用的崩溃日志。你的 lambda 的内部实现(即{ }你分配它时的内部实现)不能引用稍后将被释放的对象。一个经典的例子是:

// In your MyActivity.kt…
myLambda = { handleLambdaCallback() } 
…

private fun handleLambdaCallback() {
    …
}
Run Code Online (Sandbox Code Playgroud)

这将在运行时崩溃,因为handleLambdaCallback是隐式访问this,这将触发递归序列化它可以访问的整个对象图的尝试,这将在序列化期间的某个时刻失败。

这个问题的一个解决方案是在 lambda 中发送一个引用。例子:

// In your MyActivity.kt…
myLambda = { fragment -> (fragment.activity as MyActivity).handleLambdaCallback() }
…

private fun handleLambdaCallback() {
    …
}
Run Code Online (Sandbox Code Playgroud)

这样,我们在调用 lambda 时计算引用,而不是在分配时计算引用。绝对不是最干净的解决方案,但它是我能想到的最好的解决方案,而且它有效。

随时提出改进和替代解决方案的建议!

  • 如果您尝试序列化捕获 `this` 的 lambda,它将尝试递归序列化可从中访问的整个对象图。我希望它立即失败,而不是在反序列化时。所指对象是 GCd 不是问题,因为分块的重点是重建死对象。 (3认同)