有没有办法在活动之间传递函数引用?

时间:2017-08-10 15:14:31

标签: android kotlin

有没有一种方法可以在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()
    }
}

感谢大家的帮助!

5 个答案:

答案 0 :(得分:4)

如果tryAgainFunctionSerializable,则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)

,我在项目中一直使用一种简单的解决方案。这个想法是将所有引用类型/对象包装在ParcalabeSerializable的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))

您还可以根据使用情况传递weaksoft引用。

用例1:

在接收方活动需要使用该对象(例如可能被垃圾回收)之前,不能保证该对象(在这种情况下为objectA)已经在内存中。

在这种情况下,对象将保留在内存中,直到您明确删除强引用为止。 在此包装器中传递IntentBundle并以直接的方式访问,例如(伪代码)

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实例时传递softTrackedReference引用:

// Removing the strong reference is not needed if a week reference is tracked, e.g. 
val trackedObject = TrackedReference(WeakReference(objectA)) 
trackedObject.get?.doSomething()

最好了解为什么Intent要求ParcelableSerializable,以及在给定情况下使用此变通办法的最佳方法是什么。

序列化对象以允许活动之间的 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