kotlin 是否有方法在活动之间传递函数引用?

dba5bblo  于 2023-08-06  发布在  Kotlin
关注(0)|答案(7)|浏览(204)

有没有一种方法可以在Kotlin和Android中绑定函数引用,以便可以从其他片段调用这些函数?例如,我的fragment factory方法看起来像这样:

fun newInstance(tryAgainFunction: () -> Unit): TimeOutHandlerFragment {
        val fragment = TimeOutHandlerFragment()
        val bundle = Bundle()

        return fragment
    }

字符串
我希望能够将我的tryAgainFunction保存在bundle中以供进一步检索。
多谢了!

编辑

最后,最合适的解决方案是使用热键的答案,然后在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()
    }
}


感谢大家的帮助!

7rfyedvj

7rfyedvj1#

如果tryAgainFunctionSerializable,则you can put it into the bundle使用bundle.putSerializable("try_again_function", tryAgainFunction);
如果它是一个函数引用(SomeClass::someFunction)或lambda,则它实际上是Serializable。但如果它是函数接口() -> Unit的一些自定义实现,则可能不是,因此您应该检查它并处理它不是的情况。

pdsfdshx

pdsfdshx2#

2022年更新:
1.我原来的方法有一个缺点,在我的演示中,如果我按下home键将应用程序置于后台,它将崩溃NotSerializableException
1.实际上还有另一个API只是为了监听另一个Fragment的变化,那就是FragmentResultListener,官方教程在这里:https://youtu.be/oP-zXjkT0C0?t=468的数据。
1.如果FragmentResultListener不能满足您的需求,Shared ViewModel是最终的解决方案:https://youtu.be/THt9QISnIMQ

1.您需要将id 'kotlin-parcelize'添加到您的build.gradle(app)中,如下所示:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'kotlin-parcelize'    //here
}

字符串

2.创建一个Class来封装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()

Demo(所有Fragment):https://youtu.be/0OnaW4ZCnbk

6ss1mwsb

6ss1mwsb3#

也许太晚了,但:
你有没有试过把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() }
}

字符串

eyh26e7m

eyh26e7m4#

不,正如其他人所说,没有办法将函数字面量传递给Bundle
但是由于enum实现了Serializable,因此可以传递可以保存一些方法的枚举变量。尝试以下代码;

enum class SampleEnum(val f: () -> Unit) {
    Sample0({
        Log.d("enum Test", "sample 0")
    }),
    Sample1({
        Log.d("enum Test", "sample 1")
    }),
}

字符串
在你的from-activity(或片段)中;

bundle.putSerializable("sample_enum",SampleEnum.Sample0)


在你的to-activity(或片段)中;

// invoke your fuction
(bundle.getSerializable("sample_enum") as SampleEnum).f()

oyxsuwqo

oyxsuwqo5#

是的,有一个简单的解决方案,我一直在我的项目中使用。这个想法是将任何引用类型/对象 Package 在一个warper类中,该类是ParcalabeSerializable,而不实际实现底层引用类型的封送。下面是 Package 器类以及如何使用它而不会导致任何潜在的内存泄漏:

@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
    }

}

字符串
任何对象都可以 Package 在TrackedReference对象中,并作为可序列化或可打包的对象传递:

class ClassA {
    fun doSomething { }
}

// Add instance of ClassA to an intent
val objectA = ClassA()
intent.putExtra("key", TrackedReference(objectA))


您还可以根据用例传递weaksoft引用。

用例一:

对象(在本例中为objectA)不能保证在接收器活动需要使用它的时候(例如:可能是垃圾收集)。
在这种情况下,对象将保留在内存中,直到显式移除强引用。在这个 Package 器中传入一个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()

用例二:

对象保证在需要时在内存中,例如保证容纳片段的活动在片段的生命周期内保持在存储器中。
或者,我们不关心对象是否被垃圾回收,我们不想成为它存在的原因。在这种情况下,在初始化TrackedReference示例时传递weaksoft引用:

// 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,以及在给定场景中使用此解决方案的最佳方法是什么。
序列化对象以允许活动之间的 * 委托 * 或 * 回调 * 通信当然不是理想的。
这里是Intent上的 * 文档 *,但简单地说,Intent被传递到 *Android系统 *,然后查找如何处理它,并在这种情况下启动下一个Activity(就接收Intent的 *Android系统 * 而言,它可以是另一个应用程序)。因此,系统需要确保Intent中的所有内容都可以从地块中重建。

咆哮:

IMO,Intent作为IPC(进程间通信)的更高级别抽象可能在内部方便和高效,但以这些限制为代价。这可能是比较苹果和橘子,但在iOS中,ViewControllers(类似于 Activities)就像任何类一样,可以传递任何Type(值或引用)作为参数。然后,开发人员负责避免可能阻止ARC(内存管理)释放未使用的引用的潜在引用周期。

w7t8yxp5

w7t8yxp56#

1 -创建接口并扩展serializable

interface ItemClickListener : Serializable {
    fun onItemClick(item: ItemModel)
}

字符串
2 -传递到片段、dialogFragment或BottomSheetDialogFragment的示例

companion object {

    const val KEY = "itemClickListener"

    @JvmStatic
    fun newInstance(itemClickListener: ItemClickListener) =
        ItemFragment().apply {
            arguments = Bundle().apply {                  
                putSerializable(KEY, itemClickListener)
            }
        }
}


3 - in fragment,dialogFragment或BottomSheetDialogFragment,onCreate

//declare nullable variable
private var itemClickListener: ItemClickListener? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    arguments?.let {      
        itemClickListener = it.getSerializable(KEY, ItemClickListener::class.java)
    }
}


通过这样做,如果设备更改dark mode or light modeconfiguration changescreen rotation,您将是安全的。

70gysomp

70gysomp7#

但是,如果无法将函数文字放入Bundle中,则可以通过Fragment中类似setter的函数将函数传递给Fragment

fun setTryAgainFunction(tryAgainFunction: () -> Unit) {

}

字符串

相关问题