【问题标题】:How can I pass a Lambda to the MyWorkManager class?如何将 Lambda 传递给 MyWorkManager 类?
【发布时间】:2021-04-18 21:30:47
【问题描述】:

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

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

【问题讨论】:

    标签: android kotlin lambda android-workmanager


    【解决方案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): 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()
        }
    }
    

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

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

    【讨论】:

    • 什么是 BlurWorker 和 _testContext
    • 对不起,只是复制和粘贴错误。让我知道这是否更清楚
    【解决方案2】:

    我认为值得重新审视您的问题,看看您正在尝试做什么以及为什么它存在根本缺陷。您正在尝试将 lambda 从 Activity 传递给工作人员,这是在后台运行的东西,即使 Activity 不再存在。这没有任何意义。请不要这样做——它只会导致内存泄漏、崩溃和/或奇怪的错误,这些错误会让您难以追踪。请记住,当操作系统告诉您的应用运行它们时,需要能够从头开始创建工作者。

    【讨论】:

      【解决方案3】:

      实际上,您可以仅使用 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)
      
      


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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-25
        • 1970-01-01
        • 1970-01-01
        • 2020-12-31
        相关资源
        最近更新 更多