Kotlin - passing function as parameter via Intent
我在kotlin扩展文件中具有此功能以传递方法,但是它不起作用。请解释一下它的正确制作方法,请尝试以下操作:
1 2 3 4 5 6 7 8 9
| 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接口
1 2 3 4 5
| public interface ErrorClientListener extends Serializable {
void tryAgainFunction();
} |
和我需要监听的活动单击按钮,然后重试发送请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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()
}
}
} |
- 有趣的问题!我知道可以将lambda序列化为Serializable
-
在活动之间打包interfaces是很奇怪的,绝对是不可取的。它可能未在Activity A和Activity B之间进行序列化的原因之一是因为该对象是在Activity A中创建的,被视为匿名类创建,并且Activity A保留了对该对象的引用,因此阻止了该对象的出现。序列化。这很好,因为您可以在接口回调中创建对对象的引用,而这些引用又将通过实例化该类的引用来保留。因此,垃圾收集器将无法在这些对象上运行收集并释放空间。导致大量内存泄漏。
解决问题的另一种方法可能是使用干净的体系结构和Singleton类模式,这两个活动都可以访问它们,并且仅通过说一次Activity A:
实例化一次
1 2 3 4 5 6 7 8 9 10 11 12
| 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() }
}
} |
您可以在活动中定义
1 2 3 4 5 6 7 8 9
| class ActivityA : AppCompatActivity() {
fun onError() {
SingletonErrorHandler.instance.isError = true
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.a_activity)
}
} |
活动B
1 2 3 4 5 6 7 8 9 10
| 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()
}
} |
434511
我有同样的问题。如HawkPriest的答案中所述,您的对象不可序列化,因为它是一个匿名类。解决此问题的另一种方法是简单地实现一个实现您的接口的非匿名类。这是我的代码:
接口
1 2 3
| interface MyInterface : Serializable {
fun instruction()
} |
Class
1 2 3 4 5
| class MyClass : MyInterface {
override fun instruction() {
// does something
}
} |
通话活动
1 2 3 4
| val myObject = MyClass()
val intent = Intent(context, MyActivity::class.java).putExtra("Tag", myObject)
context.startActivity(intent) |
活动
1 2 3 4 5
| override fun onCreate(savedInstanceState: Bundle?) {
val myObject = intent.getSerializableExtra("Tag") as MyInterface
myObject.instruction()
} |
关于注释中提到的"本机资源",您可以使instruction接受参数或将其传递给MyObject。
P.S。 Singleton解决方案存在的问题:
-
Singleton不符合进行垃圾收集的资格,这意味着它不再需要就可以继续生存。 (对此不是100%的肯定,但这就是我从此答案中得到的内容)
-
使用单例将意味着您不能为活动提供"多种用途"。如果使用了一个接口,则它将能够使用该接口的多个不同实现。如果不考虑您提出的解决方案,则在您的单例中不使用接口体系结构的情况下,单例将无法提供此功能。
434511
您可以编写工厂方法来启动活动,例如android studio为片段创建生成工厂方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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()
}
} |
注意:完成不是强制性的。所以我将其设置为可为空。如果您在不使用工厂方法的情况下启动活动,应用程序将不会崩溃。
在活动之间打包interfaces是很奇怪的,绝对是不可取的。它可能未在Activity A和Activity B之间进行序列化的原因之一是因为该对象是在Activity A中创建的,被视为匿名类创建,并且Activity A保留了对该对象的引用,因此阻止了该对象的出现。序列化。这很好,因为您可以在接口回调中创建对对象的引用,而这些引用又将通过实例化该类的引用来保留。因此,垃圾收集器将无法在这些对象上运行收集并释放空间。导致大量内存泄漏。
解决问题的另一种方法可能是使用干净的体系结构和Singleton类模式,这两个活动都可以访问它们,并且仅通过说一次Activity A:
实例化一次
1 2 3 4 5 6 7 8 9 10 11 12
| 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() }
}
} |
您可以在活动中定义
1 2 3 4 5 6 7 8 9
| class ActivityA : AppCompatActivity() {
fun onError() {
SingletonErrorHandler.instance.isError = true
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.a_activity)
}
} |
活动B
1 2 3 4 5 6 7 8 9 10
| 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或在ActivityA的onError()中设置的SingletonErrorHandler内部的接口,并在doOnError()中调用callback?.invoke()
-
太感谢了!这就是我所需要的!结果非常好!))
-
不建议在Android中使用Sigleton,因为这样会导致状态丢失,内存泄漏等。这里有一篇文章讨论了该问题。 medium.com/@programmerr47/singletons-in-android-63ddf972a7e7
-
我同意单例并不总是一个好的解决方案。如果可以通过作用域依赖注入解决该问题,那将是最好的,一旦依赖任务完成,就可以清除其引用。这也意味着您的状态突变对使用它们的类具有更高的可见性。或者,您也可以使用typedMaps并传递可序列化的对象not functions,因为它们会保留引用,而函数本身是匿名的,因此它们的引用会泄漏
您可以编写工厂方法来启动活动,例如android studio为片段创建生成工厂方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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()
}
} |
注意:完成不是强制性的。所以我将其设置为可为空。如果您在不使用工厂方法的情况下启动活动,应用程序将不会崩溃。
我有同样的问题。如HawkPriest的答案中所述,您的对象不可序列化,因为它是一个匿名类。解决此问题的另一种方法是简单地实现一个实现您的接口的非匿名类。这是我的代码:
接口
1 2 3
| interface MyInterface : Serializable {
fun instruction()
} |
Class
1 2 3 4 5
| class MyClass : MyInterface {
override fun instruction() {
// does something
}
} |
通话活动
1 2 3 4
| val myObject = MyClass()
val intent = Intent(context, MyActivity::class.java).putExtra("Tag", myObject)
context.startActivity(intent) |
活动
1 2 3 4 5
| override fun onCreate(savedInstanceState: Bundle?) {
val myObject = intent.getSerializableExtra("Tag") as MyInterface
myObject.instruction()
} |
关于注释中提到的"本机资源",您可以使instruction接受参数或将其传递给MyObject。
P.S。 Singleton解决方案存在的问题:
Singleton不符合进行垃圾收集的资格,这意味着它不再需要就可以继续生存。 (对此不是100%的肯定,但这就是我从此答案中得到的内容)
使用单例将意味着您不能为活动提供"多种用途"。如果使用了一个接口,则它将能够使用该接口的多个不同实现。如果不考虑您提出的解决方案,则在您的单例中不使用接口体系结构的情况下,单例将无法提供此功能。