Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/202.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Android中保存和恢复lambdas?_Android_Kotlin_State Restoration - Fatal编程技术网

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

如何在Android中保存和恢复lambdas?,android,kotlin,state-restoration,Android,Kotlin,State Restoration,在Android中实现状态恢复时,如何保存和恢复lambda 我尝试将其保存为可序列化和可打包,但它会引发编译错误 有什么方法可以保存和恢复它们,或者我应该寻求其他方法吗?Kotlin lambdas实现Serializable,因此它们不能像以下那样保存: override fun onSaveInstanceState(outState: Bundle) { outState.putSerializable("YOUR_TAG", myLambda as Serializable)

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

我尝试将其保存为可序列化和可打包,但它会引发编译错误


有什么方法可以保存和恢复它们,或者我应该寻求其他方法吗?

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
的任何生命周期事件中完成,因为这只是一个示例)

一些注意事项:

  • 保存它们时,需要对它们进行强制转换,否则编译器会抱怨(出于某种原因)
  • 需要导入java.io.Serializable
  • 将其强制转换回lambda类型的方法将抛出警告
    Unchecked cast:Serializable?到您的lambdatype
    。此强制转换是安全的(假设您正确推断为空!),因此您可以使用
    @Suppress(“UNCHECKED_cast”)
  • MyObject
    必须是
    Serializable
    Parcelable
    ,否则它会在运行时崩溃
现在有一个细节在任何地方都没有被告知,并且在运行时崩溃,没有有用的崩溃日志。lambda的内部实现(即分配它时,
{}
中的内容)不能引用稍后将释放的对象。 一个典型的例子是:

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

private fun handleLambdaCallback() {
    …
}
这将在运行时崩溃,因为
handleLambdaCallback
隐式访问
,这将触发尝试递归序列化可由其访问的整个对象图,这将在序列化期间的某个点失败

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

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

private fun handleLambdaCallback() {
    …
}
这样,我们在调用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" }

并稍后使用还原的
名称
参数调用

如果您尝试序列化捕获
的lambda,它将尝试递归序列化可从它访问的整个对象图。我预计这将立即失败,而不是在反序列化时。作为GCd的引用对象不是问题,因为整个包化点都是重建死对象。您必须将lambda强制转换为可序列化的
Serializable
的原因应该是,这首先向Kotlin发出信号,以创建可序列化的lambda。lambda在默认情况下不可序列化。没错,它在序列化时失败,而不是在反序列化时失败。对我来说,序列化整个对象图的有趣之处在于,当活动转到onStop lifecycle事件时,它失败了。此外,我还能够在带有参数bundle的片段中成功地传递、检索和类型转换lambda。我不理解lambda方法是如何固有地不稳定并且容易发生内存泄漏的。我认为我的答案改善了内存泄漏,特别是因为现在必须不引用
。但是,我认为您的方法会使子对象(例如片段)意识到其父对象(即片段必须调用
(activity as MyActivity)。sayHello(name)
),这显然是不好的。要么这样,要么我们就采用“委托”(监听器)方法。我遗漏了什么吗?@MarkoTopolnik从定义上讲,lambdas没有错,但是从你开始在作用域中使用变量而不是参数的那一刻起,事情就变得一团糟了:@MarkoTopolnik 1。你不能只看lambda的定义就知道它应该做什么,@MarkoTopolnik 2。出于同样的原因,很难进行测试,很难知道要模拟的依赖项(参数)是什么,您必须将它们定义为范围中可用的变量(而不仅仅是调用函数)