Kotlin - 通过 Intent 将函数作为参数传递

Posted

技术标签:

【中文标题】Kotlin - 通过 Intent 将函数作为参数传递【英文标题】:Kotlin - passing function as parameter via Intent 【发布时间】:2018-11-29 14:14:20 【问题描述】:

我在 kotlin 扩展文件中有这个函数来传递方法,但它不起作用。请解释一下它是如何正确制作的,我试试这个:

fun showErrorClientScreen(context: Context, action : () -> Unit) 
    val intent = Intent(context, RestClientErrorActivity::class.java)

    val bundle = Bundle()
    bundle.putSerializable(UPDATE_CLIENT_ERROR, ErrorClientListener   action )
    intent.putExtra(UPDATE_CLIENT_ERROR_BUNDLE, bundle)

    context.startActivity(intent)

使用java接口

public interface ErrorClientListener extends Serializable 

    void tryAgainFunction();


以及我需要监听单击按钮并再次尝试发送请求的活动:

class RestClientErrorActivity: BaseActivity(), View.OnClickListener 

    private lateinit var errorClientListener: ErrorClientListener

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_rest_client_error)

        try 
            val bundle = intent.getBundleExtra(UPDATE_CLIENT_ERROR_BUNDLE)
            errorClientListener = bundle?.getSerializable(UPDATE_CLIENT_ERROR) as ErrorClientListener
         catch (e: Exception) 
            e.message
        
    

    override fun onClick(v: View?) 
        when (v?.id) 
            R.id.ib_update -> errorClientListener.tryAgainFunction()
        
    


【问题讨论】:

有趣的问题!我知道在 Java 中可以将 lambda 序列化为 Serializable & Runnable。我不知道它在 Kotlin 中是如何工作的。但是想知道将 lambda 表达式序列化以传递给 Intent 是否是个好主意?使用 EventBus 或 Observables 怎么样? 谢谢,但我想使用原生资源) 【参考方案1】:

在活动之间打包interfaces很奇怪,绝对不可取。它可能不在Activity AActivity B 之间序列化的一个原因是因为该对象是在Activity A 中创建的,它被视为匿名类创建并且Activity A 持有对该对象的引用,因此阻止它被连载。这很好,因为您可以在接口回调中创建对对象的引用,而这些对象的引用又将由实例化它的类持有。因此,垃圾收集器将无法对这些对象运行收集并释放空间;导致大量内存泄漏。

解决您的问题的另一种方法可能是使用干净的架构和Singleton 类模式,这两个活动都可以访问,并且只能通过Activity A 实例化一次:

class SingletonErrorHandler private constructor()
    var isError = false

    fun doOnError() 
        // do non view related stuff
        // like a network call or something          
    

    companion object 
        val instance by lazy  SingletonErrorHandler() 
    

在你可以定义的活动中

class ActivityA : AppCompatActivity() 
    fun onError() 
        SingletonErrorHandler.instance.isError = true
    
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.a_activity)
    

在活动 B 中

class ActivityB : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.b_activity)

        val errorHandler = SingletonErrorHandler.instance
        if(errorHandler.isError)
             errorHandler.doOnError()
    

【讨论】:

谢谢,但是如何具体将函数作为参数传递使用您的方法? 你需要执行的action是什么? 在我的情况下,该操作可以是任何方法,当我重新连接时,我必须发送不同的请求 如果您不更新 ActivityA 中的任何视图,那么您可以使用 lambda 变量 var callback : (() -> Unit)? = null 或在 onError()ActivityA 中设置的 SingletonErrorHandler 内的接口并调用callback?.invoke()doOnError() 非常感谢!这是我需要的!结果非常好!))【参考方案2】:

我遇到了同样的问题。如HawkPriest's Answer 中所述,您的对象不可序列化,因为它是一个匿名类。解决此问题的另一种方法是简单地实现一个实现您的接口的非匿名类。这是我的代码:

界面

interface MyInterface : Serializable 
    fun instruction()

class MyClass : MyInterface 
    override fun instruction() 
        // does something
    

通话活动

val myObject = MyClass()
val intent = Intent(context, MyActivity::class.java).putExtra("Tag", myObject)

context.startActivity(intent)

活动

override fun onCreate(savedInstanceState: Bundle?) 
    val myObject = intent.getSerializableExtra("Tag") as MyInterface

    myObject.instruction()

关于您评论中提到的“原生资源”,您可以让您的instruction 带参数或将它们传递给您的MyObject

附:我在使用 Singleton 解决方案时遇到的问题:

    Singleton 不符合垃圾收集条件,这意味着它在不再需要后仍然存在。 (不是 100% 确定这一点,但这就是我从 this answer 得到的) 使用单例意味着您的活动不能有“多种不同的用途”。如果使用一个接口,它是能够使用该接口的多个不同实现。考虑到我提出的解决方案,如果不在您的单例中使用接口架构,单例将无法提供此功能,这将再次使其变得不必要。

【讨论】:

【参考方案3】:

您可以编写工厂方法来启动 Activity,例如 android studio 生成工厂方法来创建片段。

    class RestClientErrorActivity : AppCompatActivity() 

    companion object 
        private var completion: (() -> Unit)? = null
        fun start(context: Context, completion: (() -> Unit)?) 
            RestClientErrorActivity.completion = completion
            val bundle = Bundle()
    intent.putExtra(UPDATE_CLIENT_ERROR_BUNDLE, bundle)
     context.startActivity(intent)
        
    

    private lateinit var retryButton: Button

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        retryButton = findViewById(R.id.btn_retry)
    

    fun onRetryClick(view: View) 
        finish()
        completion?.invoke()
    

注意:完成不是强制性的。所以我把它设为可以为空。如果您在不使用工厂方法的情况下启动活动,应用程序不会崩溃。

【讨论】:

以上是关于Kotlin - 通过 Intent 将函数作为参数传递的主要内容,如果未能解决你的问题,请参考以下文章

如何通过lambda作为Intent的额外内容?

通过intent-filter kotlin从其他应用程序接收数据?

Kotlin函数 ⑨ ( Kotlin 语言中的闭包概念 | Java 语言中函数作为参数的替代方案 )

Kotlin Spinner OnItemSelected 意图

Kotlin:如何将一个函数作为参数传递给另一个函数?

如何通过 kotlin 中的意图传递自定义对象