有没有一种方法可以在Kotlin和Android中捆绑函数引用,以便可以从其他片段调用这些函数? 例如,我的片段工厂方法如下所示:
fun newInstance(tryAgainFunction: () -> Unit): TimeOutHandlerFragment {
val fragment = TimeOutHandlerFragment()
val bundle = Bundle()
return fragment
}
我希望能够在包中保存我的tryAgainFunction以便进一步检索。
非常感谢!
修改
最后,最合适的解决方案是使用热键的答案,然后在onViewCreated中使用传递的函数初始化一个侦听器。完整的代码如下:
companion object {
val CALLBACK_FUNCTION: String = "CALLBACK_FUNCTION"
fun newInstance(tryAgainFunction: () -> Unit): TimeOutHandlerFragment {
val fragment = TimeOutHandlerFragment()
val bundle = Bundle()
bundle.putSerializable(CALLBACK_FUNCTION, tryAgainFunction as Serializable)
fragment.arguments = bundle
return fragment
}
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
try {
val callback: () -> Unit = arguments.getSerializable(CALLBACK_FUNCTION) as () -> Unit
btnTryAgain.setOnClickListener { callback.invoke() }
} catch (ex: Exception) {
// callback has a wrong format
ex.printStackTrace()
}
}
感谢大家的帮助!
答案 0 :(得分:4)
如果tryAgainFunction
为Serializable
,则you can put it into the bundle
使用bundle.putSerializable("try_again_function", tryAgainFunction);
。
如果是函数引用(Serializable
)或lambda,它实际上是SomeClass::someFunction
。但它可能不是,如果它是函数接口() -> Unit
的一些自定义实现,那么你应该检查它并处理它不是的情况。
答案 1 :(得分:3)
也许为时已晚,但是:
您是否尝试过将val callback
放在伴随对象中?
解决方案可能类似于:
companion object {
private lateinit var callback
fun newInstance(tryAgainFunction: () -> Unit): TimeOutHandlerFragment {
val fragment = TimeOutHandlerFragment()
this.callback = tryAgainFunction
return fragment
}
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btnTryAgain.setOnClickListener { callback.invoke() }
}
答案 2 :(得分:0)
但是,如果无法将函数文字放入Bundle
,则可以通过Fragment
中的setter函数将函数传递给Fragment
。
fun setTryAgainFunction(tryAgainFunction: () -> Unit) {
}
答案 3 :(得分:0)
是,我在项目中一直使用一种简单的解决方案。这个想法是将所有引用类型/对象包装在Parcalabe
和Serializable
的warper类中,而无需实际实现底层引用类型的封送处理。这是包装器类,以及如何使用它而不引起任何潜在的内存泄漏:
@Parcelize @kotlinx.serialization.Serializable
data class TrackedReference<ReferenceType: Any> private constructor(
private var uniqueID: Int = -1
) : Serializable, Parcelable {
constructor(reference: ReferenceType) : this() {
uniqueID = System.identityHashCode(this)
referenceMap.set(uniqueID, reference)
}
val get: ReferenceType?
get() = referenceMap.get(uniqueID) as? ReferenceType
fun removeStrongReference() {
get?.let { referenceMap.set(uniqueID, WeakReference(it)) }
}
companion object {
var referenceMap = hashMapOf<Int, Any>()
private set
}
}
任何对象都可以包装在TrackedReference
对象中,并以可序列化或可打包的方式传递:
class ClassA {
fun doSomething { }
}
// Add instance of ClassA to an intent
val objectA = ClassA()
intent.putExtra("key", TrackedReference(objectA))
您还可以根据使用情况传递weak
或soft
引用。
用例1:
在接收方活动需要使用该对象(例如可能被垃圾回收)之前,不能保证该对象(在这种情况下为objectA
)已经在内存中。
在这种情况下,对象将保留在内存中,直到您明确删除强引用为止。
在此包装器中传递Intent
或Bundle
并以直接的方式访问,例如(伪代码)
class ClassA {
fun doSomething { }
}
// Add instance of ClassA to an intent
val objectA = ClassA()
intent.putExtra("key", TrackedReference(objectA))
// Retrieve in another activity
val trackedObjectA = intent.getSerializable("key") as TrackedReference<ClassA>
trackedObjectA.get?.doSomething()
// Important:- to prevent potential memory leak,
// remove *strong* reference of a tracked object when it's no longer needed.
trackedObjectA.removeStrongReference()
用例2:
保证对象在需要时位于内存中,例如包含片段的活动可以保证在片段的生命周期内保留在内存中。
或者,我们不在乎对象是否被垃圾回收,我们也不想成为其存在的原因。在这种情况下,初始化weak
实例时传递soft
或TrackedReference
引用:
// Removing the strong reference is not needed if a week reference is tracked, e.g.
val trackedObject = TrackedReference(WeakReference(objectA))
trackedObject.get?.doSomething()
最好了解为什么Intent
要求Parcelable
或Serializable
,以及在给定情况下使用此变通办法的最佳方法是什么。
序列化对象以允许活动之间的 delegation 或 callback 通信当然不是理想的。
这是Intent
上的 documentation ,但简单地说,Intent
被传递到 Android系统 -处理它,在这种情况下,启动下一个活动(就接收意图的 Android系统而言,它可以是另一个应用程序)。因此,系统需要确保可以从宗地中重构意图内的所有内容。
Rant:
IMO,Intent
作为IPC(进程间通信)的高层抽象,在内部可能既方便又高效,但是却以这些限制为代价。
也许是将苹果与桔子进行比较,但在iOS中, ViewControllers (类似于 Activities )就像任何类一样,可以通过任何Type
(值或引用)作为一个争论。然后,开发人员负责避免可能导致ARC(内存管理)释放未使用的引用的潜在参考周期。
答案 4 :(得分:0)
有可能在 2021 年。
1.您需要将 id 'kotlin-parcelize'
添加到您的 build.gradle(应用程序)中,如下所示:
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'kotlin-parcelize' //here
}
2.创建一个类来包装您的 Lambda 函数:
@Parcelize
class Listener(val clickAction: () -> Unit) : Parcelable {
fun onClick() = clickAction()
}
3.通过您的 Lambda:
val bundle = Bundle().apply {
putParcelable("listener", Listener(clickAction))
}
private val clickAction: () -> Unit = {
//do your stuff
}
4.检索它:
arguments?.getParcelable<Listener>("listener")?.onClick()
演示(所有Fragment
):https://youtu.be/0OnaW4ZCnbk