如何在Android中保存和还原Lambda?

时间:2018-12-27 03:55:04

标签: android kotlin state-restoration

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

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

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

2 个答案:

答案 0 :(得分:1)

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是必需的。
  • 将其强制转换回lambda类型的方法将引发警告Unchecked cast: Serializable? to YourLambdaType。此强制转换是安全的(假设您正确推断了可空性!),因此您可以使用@Suppress("UNCHECKED_CAST")
  • 来安全地禁止此警告。
  • MyObject必须为SerializableParcelable,否则它将在运行时崩溃。

现在,有一个细节在任何地方都不会告诉您,并且在运行时崩溃,没有有用的崩溃日志。 Lambda的内部实现(即{ }里面的内容)不能引用稍后将被释放的对象。 一个典型的例子是:

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

private fun handleLambdaCallback() {
    …
}

这将在运行时崩溃,因为handleLambdaCallback隐式访问this,这将触发对它可以访问的整个对象图进行递归序列化的尝试,这在序列化过程中有时会失败。 / p>

此问题的一种解决方案是在lambda中发送引用。示例:

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

private fun handleLambdaCallback() {
    …
}

这样,我们在调用lambda时而不是在分配引用时计算引用。绝对不是最干净的解决方案,但这是我所能提供的最好的解决方案,并且可以使用。

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

答案 1 :(得分:0)

  

我应该寻求其他方法吗?

是的,没有充分的理由这样做,您的代码将不易于测试,并且可能导致内存泄漏。

而不是保存函数,而是保存需要保存的参数(即 scope 中的变量),然后像平常一样调用函数。

示例

代替做

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参数调用