如何将Lambda传递给MyWorkManager类?

时间:2019-04-10 05:51:05

标签: android kotlin lambda android-workmanager

我正在将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处引用了该类的构造函数,但我认为这不是正确的方法。

3 个答案:

答案 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函数被调用。