【发布时间】:2019-05-25 03:49:24
【问题描述】:
在 Android 中实现状态恢复时,如何保存和恢复 lambda?
我尝试将其保存为 Serializable 和 Parcelable,但它会引发编译错误。
有什么方法可以保存和恢复它们,还是我应该寻求其他方法?
【问题讨论】:
标签: android kotlin state-restoration
在 Android 中实现状态恢复时,如何保存和恢复 lambda?
我尝试将其保存为 Serializable 和 Parcelable,但它会引发编译错误。
有什么方法可以保存和恢复它们,还是我应该寻求其他方法?
【问题讨论】:
标签: android kotlin state-restoration
Kotlin lambdas 实现了Serializable,所以它们不能像这样保存:
override fun onSaveInstanceState(outState: Bundle) {
outState.putSerializable("YOUR_TAG", myLambda as Serializable)
super.onSaveInstanceState(outState)
}
同样,要恢复它们:
override fun onCreate(savedInstanceState: Bundle?) {
myLambda = savedInstanceState?.getSerializable("YOUR_TAG") as (MyObject) -> Void
super.onCreate(savedInstanceState)
}
(这显然可以在为您提供savedInstanceState 的任何生命周期事件中完成,因为这只是一个示例)
一些注意事项:
import java.io.Serializable 为必填项。Unchecked cast: Serializable? to YourLambdaType。这个演员是安全的(假设你正确地推断出可空性!),所以你可以通过使用@Suppress("UNCHECKED_CAST") 安全地抑制这个警告
MyObject 必须是 Serializable 或 Parcelable,否则会在运行时崩溃。现在有一个细节在任何地方都没有被告知,并且在运行时崩溃并且没有有用的崩溃日志。 lambda 的内部实现(即分配它时 { } 内部的内容)不能引用稍后将被释放的对象。
一个经典的例子是:
// In your MyActivity.kt…
myLambda = { handleLambdaCallback() }
…
private fun handleLambdaCallback() {
…
}
这将在运行时崩溃,因为handleLambdaCallback 正在隐式访问this,这将触发尝试递归序列化它可访问的整个对象图,这会在序列化期间的某个时间点失败。
解决这个问题的一个方法是在 lambda 中发送一个引用。示例:
// In your MyActivity.kt…
myLambda = { fragment -> (fragment.activity as MyActivity).handleLambdaCallback() }
…
private fun handleLambdaCallback() {
…
}
这样,我们在调用 lambda 时计算引用,而不是在分配时计算引用。绝对不是最干净的解决方案,但它是我能想到的最好的解决方案,而且它有效。
随时提出改进和替代解决方案的建议!
【讨论】:
this 的 lambda,它将尝试递归序列化可从它访问的整个对象图。我希望它会立即失败,而不是在反序列化时。 GCd 所指的对象不是问题,因为分块的重点是重建死对象。
Serializable 的原因应该是这首先向 Kotlin 发出信号以创建可序列化的 lambda。默认情况下,Lambda 是不可序列化的。
我应该寻求其他方法吗?
是的,没有一个很好的理由这样做,你的代码不容易测试,你可能会引入内存泄漏。
不保存函数,而是保存需要保存的参数(即作用域中的变量),然后像往常一样调用函数。
示例
而不是做
val name = "John Smith"
val sayHello = { "Hi there, $name" }
startActivity(Intent().apply { putExtra("GREETER", sayHello as Serializable) })
创建一个可以在其他地方使用的函数
fun sayHello(name: String) = { "Hi there, $name" }
稍后使用恢复的name 参数调用
【讨论】:
this。但是,我认为您的方法使孩子(例如片段)知道其父母(即片段必须调用(activity as MyActivity).sayHello(name)),这显然不好。要么这样,要么我们会导致“委托”(监听器)方法。我错过了什么吗?