WorkManager не работает в библиотеке, когда клиент вызывает WorkManager.initialize ()

Я пишу библиотеку для Android, в которой используется WorkManager. Где-то в коде я называю что-то вроде этого:

val uploadTripRequest = OneTimeWorkRequest.Builder(UploadTripWorker::class.java)
    .setConstraints(someConstraints)
    .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, OneTimeWorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS)
    .build()

WorkManager.getInstance(context)
    .enqueueUniqueWork(WORKER_NAME, ExistingWorkPolicy.REPLACE, uploadTripRequest)

В моем build.gradle у меня есть: implementation 'androidx.work:work-runtime:2.2.0'

В норме все работает отлично. Проблемы возникают, когда клиентское приложение вызывает https://developer.android.com/reference/kotlin/androidx/work/WorkManager.html#initialize, например:

WorkManager.initialize(context, Configuration.Builder().setWorkerFactory(myWorkerFactory).build())

Когда это вызывается, WorkManager из моей библиотеки не работает должным образом. doWork метод не вызывается в моем UploadTripWorker, но выполняется на рабочем месте, предоставленном клиентским myWorkerFactory.

С моей точки зрения, WorkManager непригоден для использования в библиотеке, поскольку это синглтон, и кто-то может изменить его поведение с помощью метода initialize. Но я надеюсь, что ошибаюсь.

Как я могу решить эту проблему? Конечно, я не могу сказать всем своим клиентам не использовать метод WorkManager.initialize().


person apaluk    schedule 11.10.2019    source источник


Ответы (2)


WorkManager v2.1 представил класс DelegatingWorkerFactory, который может помочь в этих случаях.

Это по-прежнему требует, чтобы разработчик приложения реализовал его правильно, как вы сказали, WorkManager - это синглтон.

Ключевым моментом является то, что, если приложение требует настраиваемой инициализации и настраиваемой WorkerFactory, оно должно использовать DelegatingWorkerFactory, а затем добавить к нему собственный WorkerFactory.

Ключевым моментом здесь является то, что WorkerFactory приложения должна проверять имя рабочего класса, чтобы быть уверенным, что оно правильное (обычно вводя некоторые параметры в конструктор). Если имя класса не соответствует ожиданиям приложения, оно может просто вернуть null, а DelegatingWorkerFactory позаботится о том, чтобы найти правильный рабочий класс для создания экземпляра.

Что-то вроде (в коде приложения):

class MyWorkerFactory(
    private val myInjectedParam: InjectedParam
) : WorkerFactory() {

    override fun createWorker(
        appContext: Context,
        workerClassName: String,
        workerParameters: WorkerParameters
    ): ListenableWorker? {

        return when (workerClassName) {
            MyWorker::class.java.name ->
                MyWorker(appContext, workerParameters, myInjectedParam)
            else ->
                // Return null, so that the base class can delegate to the default WorkerFactory.
                null
        }
    }
}

Затем приложению необходимо добавить этот WorkerFactory в DelegatingWorkerFactory, который он настроил как пользовательский WorkerFactory, используя:

private fun initializeWorkManager (myInjectedParam: MyInjectedParam): WorkManager
{ 
    val appContext = getApplication<MyApplication>()
    val factory = appContext.workManagerConfiguration.workerFactory
            as DelegatingWorkerFactory
    factory.addFactory(MyWorkerFactory(myInjectedParam))

    return WorkManager.getInstance(appContext)
}

В классе приложения у вас есть:

class MainApplication : Application(), Configuration.Provider {

    val delegatingWorkerFactory: DelegatingWorkerFactory

    // Setup custom configuration for WorkManager with a DelegatingWorkerFactory
    override fun getWorkManagerConfiguration(): Configuration {
        return Configuration.Builder()
            .setMinimumLoggingLevel(android.util.Log.INFO)
            .setWorkerFactory(delegatingWorkerFactory)
            .build()
    }
}
person pfmaggi    schedule 11.10.2019

Если у вас есть Worker и WorkerFactory в проекте библиотеки, отключение инициализатора по умолчанию в манифесте библиотеки, похоже, помогает. Поскольку у библиотеки будет другой пакет, чем у приложения, инициализатор по умолчанию, кажется, срабатывает, если вы не отключите его.

<provider android:name="androidx.work.impl.WorkManagerInitializer"
          android:authorities="<library's package>.analytics.workmanager-init"
          tools:node="remove" />

Настройка, которая у меня сработала.

Мое приложение содержит:

  • Конфигурация / Провайдер
  • DelegatingWorkerFactory () настроен на указанную выше конфигурацию

Моя библиотека содержит:

  • Рабочий
  • WorkerFactory
  • WorkRequestBuilder
  • Запрос на работу

Но что меня интригует, так это аннотация в исходном коде WorkerFactory:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)

Не уверен, что это должно было предупредить меня о том, что этот метод вызывается из другого пакета.

person Vik    schedule 16.10.2020