如何将 Lambda 传递给 MyWorkManager class?

How can I pass a Lambda to the MyWorkManager class?

我正在为 android 使用 kotlin,我正在尝试创建一个通用的 Worker class,我可以在其中传递一个可以从 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 class。 是否可以使用 setInputData() of OneTimeWorkRequestBuilder class.

传递 Lambda

我提到 How to pass the worker parameters to WorkManager class 正在调用 class 的构造函数,我认为这不是正确的方法。

WorkManager 中的 Data class 仅适用于基本类型及其数组。你不能用它来传递兰巴。

一种可能的解决方案是自定义 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): MyWorker {

        return MyWorker(appContext, 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()
    }
}

记得禁用默认的 WorkManager 初始化添加到 AndroidManifest.xml:

<provider
          android:name="androidx.work.impl.WorkManagerInitializer"
          android:authorities="${applicationId}.workmanager-init"
          tools:node="remove" />

我认为值得重新审视你的问题,看看你想做什么以及为什么它存在根本性的缺陷。您正在尝试将 lambda 从 Activity 传递给工作人员,这是 运行 在后台进行的操作,即使 Activity 已不存在。这没有任何意义。请不要这样做 - 它只会导致内存泄漏、崩溃、and/or 奇怪的错误,您将很难找到这些错误。请记住,当 OS 告诉您的应用 运行 时,需要能够从头开始创建工作人员。

实际上,您可以 仅使用 WorkManager 提供给我们的内容来完成。 IMO 更改 WorkManager 初始化对于其他答案中建议的这样简单的事情来说太 complex/risk。

WorkRequests 接受 ByteArray 输入,它可以是任何对象,对吧?因此,创建一个序列化对象,包装稍后将调用的 lambda 函数

定义一个可序列化的包装器 class 传递 lambda(logRequestTime): LambdaSerializable

package com.febaisi.lambdawithworkers
import java.io.Serializable

class LambdaSerializable(val logRequestTime: () -> (Unit)): Serializable {
}


将其转换为 ByteArray 并将其 put 放入输入对象中。

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)


从工人那里打电话。简单工人

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 函数。