我正在将kotlin用于android,并且试图创建一个通用的Worker类,在其中可以传递可以从doWork()方法调用的lambda。
class BaseWorker(val context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): Result {
//Passed from the activity while creating the work request
someLambda()
return Result.success()
}
}
问题是我没有通过调用构造函数实例化BaseWorker
类。
是否可以使用setInputData()
类的OneTimeWorkRequestBuilder
传递Lambda。
我在How to pass the worker parameters to WorkManager class处引用了该类的构造函数,但我认为这不是正确的方法。
答案 0 :(得分:1)
WorkManager中的Data
类仅适用于基本类型及其数组。您不能使用它传递兰巴犬。
一个可能的解决方案是自定义WorkManager的初始化as explained in the documentation,并使用自定义的WorkerFactory将参数添加到构造函数中,以用于检索lambda。请记住,您在初始化时仅配置一次WorkManager。这意味着您可以直接将lambda作为附加参数传递,但无法为每个WorkRequest自定义它。
根据您要精确实现的目标,可以将类似的内容用作起点:
// provide custom configuration
val config = Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.setWorkerFactory(MyWorkerFactory(lambda))
.build()
//initialize WorkManager
WorkManager.initialize(this, config)
val workManager = WorkManager.getInstance()
然后拥有您的WorkerFactory:
class MyWorkerFactory(private val lambda: Unit) : WorkerFactory() {
override fun createWorker(appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters): BlurWorker {
return MyWorker(_testContext, workerParameters, lambda)
}
}
然后您可以让您的工作人员使用新的构造函数:
class MyWorker(val context: Context, workerParams: WorkerParameters, private val lambda: Unit) : Worker(context, workerParams) {
override fun doWork(): Result {
//Passed from the WorkManager's configuration
lambda()
return Result.success()
}
}
记住要禁用添加到AndroidManifest.xml
的默认WorkManager初始化:
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
答案 1 :(得分:0)
我认为值得重新审视您的问题,以查看您要尝试做的事情以及为什么它存在根本性缺陷。您正在尝试将lambda从Activity传递给工作程序,即使该Activity不再存在,它也是在后台运行的。这没有任何意义。请不要这样做-只会导致内存泄漏,崩溃和/或奇怪的错误,这些问题使您难以追踪。请记住,当操作系统告诉您的应用运行它们时,需要能够从头开始创建工作程序。
答案 2 :(得分:0)
实际上,您可以仅使用WorkManager提供给我们的内容来做到。 IMO更改WorkManager的初始化过于复杂/存在风险,对于像其他答案中所建议的那样简单的事情而言。
WorkRequests接受ByteArray输入,可以是任何对象,对吗?因此,创建一个包装了lambda函数的序列化对象,该函数稍后将被称为
定义一个可传递lambda(logRequestTime)的可序列化包装器类:LambdaSerializable
package com.febaisi.lambdawithworkers
import java.io.Serializable
class LambdaSerializable(val logRequestTime: () -> (Unit)): Serializable {
}
将其转换为ByteArray并在输入对象中放入。
val lambdaObject = LambdaSerializable { //Lambda function
Log.e("Lambda", "Request time was -> $requestTime")
}
val input = Data.Builder()
.putByteArray(SimpleWorker.LAMBDA_OBJECT, serializeObject((lambdaObject as Object)))
.build()
val simpleWorker = OneTimeWorkRequest.Builder(SimpleWorker::class.java)
.setInputData(input)
.build()
WorkManager.getInstance(applicationContext).enqueueUniqueWork("lambda_worker", ExistingWorkPolicy.KEEP, simpleWorker)
从工人那里叫它。 SimpleWorker
class SimpleWorker(context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) {
companion object {
val LAMBDA_OBJECT = "LAMBDA_OBJECT"
}
override fun doWork(): Result {
Log.e("Lambda", "Executing worker - Sleeping for 5 seconds - Compare request vs current time")
val lambdaSerializable = inputData.getByteArray(LAMBDA_OBJECT)?.let{ getByteInput(it) }
runBlocking {
delay(5000)
val sdf = SimpleDateFormat("yyyyMMdd__HH:mm:ss", Locale.getDefault())
Log.e("Lambda", "Current time is -> ${sdf.format(Date())}")
(lambdaSerializable as LambdaSerializable).logRequestTime() // High level - Calling Lambda
}
return Result.success()
}
}
完整示例:https://github.com/febaisi/LambdaWithWorkers/
检查日志以查看lambda函数被调用。