乐趣区

关于android:使用-Dagger-自定义-WorkManager

WorkManager 是一个 Android Jetpack 扩大库,它能够让您轻松布局那些可延后、异步但又须要牢靠运行的工作。对于绝大部分后盾执行工作来说,应用 WorkManager 是目前 Android 平台上的最佳实际。

如果您始终关注本系列文章,则会发现咱们曾经探讨过:

  • Android Jetpack WorkManager | Android 中文教学视频
  • WorkManager 与 Kotlin
  • WorkManager Periodicity
  • 自定义 WorkManager —— 根底概念

在本篇文章中,咱们将会探讨应用 Dagger 自定义配置相干的内容,包含:

  • 在咱们的 WorkerFactory 中应用 Dagger 注入参数
  • 按需初始化

⚠️ 本文扩大自上一篇自定义 WorkManager 的文章。强烈建议在浏览本文之前先去浏览 上一篇文章!

为什么是 Dagger

Dagger 是 Android 开发的首选依赖注入库,Google 正积极参与它的开发。如果您还没开始应用 Dagger,或者心愿理解更多无关它的信息,请查阅以下材料:官网指南、Codelab 实战教程 以及咱们近期公布的对于在 最新 Android Studio 中应用 Dagger 导航 的文章。

行文中我假如您对 Dagger 库和依赖注入概念均已有所理解。

即便您正在应用其余的依赖注入库,或者基本没有应用依赖库,本文所出现的概念仍然会对您有所帮忙。

回顾

上一篇文章 中,咱们摸索了如何自定义 WorkManager,其中包含如何应用 DelegatingWorkerFactory 将附加的参数传递到 Worker 中。在本篇文章中,让咱们看一看如何应用 Dagger 注入这些参数。

应用 Dagger 将参数注入到 WorkerFactory

如果您以后曾经在应用 Dagger 来治理依赖,那么首先须要将 Dagger 集成到您的 WorkerFactory 中。如果您应用 Dagger 在您的利用中传递 Retrofit 服务的援用,而且您想要将其传递给您的 Worker,则须要应用 Dagger 将该援用注入到自定义的 WorkerFactory 中。这样一来,WorkFactory 就能够应用 Retrofit 的援用作为额定参数来初始化您的 Worker。

假如这次咱们有了 Dagger 注入的 Retrofit 服务的援用。然而这并没有扭转 WorkManager 须要自定义工厂和自定义配置的场面。简略来说,咱们将用 Dagger 把新的参数注入到咱们的工厂中。

/* Copyright 2020 Google LLC.    
   SPDX-License-Identifier: Apache-2.0 */
@Singleton
class MyWorkerFactory @Inject constructor(service: DesignerNewsService) : DelegatingWorkerFactory() {
    init {addFactory(myWorkerFactory(service))
 // Add here other factories that you may need in your application
 // 在这里增加您利用中可能会应用的其余工厂
    }
}

⚠️ 提醒:如果想要 Dagger 可能注入这个值,咱们必须把它放进 Dagger 的图中。这就是为什么咱们给 Factory 增加了一个 @inject 注解。

本示例中,咱们在 Application 里应用一个 AppComponent 来设置 Dagger。AppComponent 稍后会在 MyApplication 中初始化,从而让 Dagger 能够进行成员注入:

/* Copyright 2020 Google LLC.    
   SPDX-License-Identifier: Apache-2.0 */
@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {

  @Component.Factory
  interface Factory {fun create(@BindsInstance context: Context): AppComponent
  }

  fun inject(application: MyApplication)
}

随后,咱们在 Application 类中注入咱们的组件:

 

/* Copyright 2020 Google LLC.    
   SPDX-License-Identifier: Apache-2.0 */
class MyApplication : Application(), Configuration.Provider {

    // other @Inject variables
    // 另一个 @inject 变量

    lateinit var appComponent: AppComponent

    override fun onCreate() {super.onCreate()
        appComponent = DaggerAppComponent.factory().create(applicationContext)
        appComponent.inject(this)
    }
   ...
}

因为 Dagger 曾经晓得如何提供 MyWorkerFactory 实例,您当初能够通过应用 @Inject 来从 Dagger 图中获取 MyWorkerFactory,并在 getWorkManagerConfiguration 办法中进行应用。

/* Copyright 2020 Google LLC.    
   SPDX-License-Identifier: Apache-2.0 */
class MyApplication : Application(), Configuration.Provider {

    @Inject lateinit var myWorkerFactory: MyWorkerFactory 
    ...

    override fun getWorkManagerConfiguration(): Configuration =
        Configuration.Builder()
                     .setMinimumLoggingLevel(android.util.Log.INFO)
                     .setWorkerFactory(myWorkerFactory)
                     .build()
    ...
}

我的项目中的其余局部放弃不变。

生产环境示例

在应用中型或大型数据库时,Dagger 的体现非常亮眼。咱们降级了 Google I/O 与 Android 开发峰会的时间表利用:iosched,使其用上 WorkManager 和 Dagger,它同时也是咱们用于展现协程 Flow 最佳实际的利用,详情请查看文章:基于 Android 开发者峰会利用的协程 Flow 最佳实际。

在 2019 Android 开发者峰会利用 中,JobScheduler 被 WorkManager 所取代,用于强制更新时间表。为了能将时间表的紧急更新强制推送至设施,咱们为利用增加了这个性能。

在这种状况下,咱们须要在 Worker 中应用的额定参数是 refreshEventDataUseCase。您能够在 github 的 iosched 仓库中的 ADSsched 分支 中查看引入了此性能的提交 (commits)。

让咱们从 Worker 自身开始,看看最重要的几局部:

ConferenceDataWorker.kt

/* Copyright 2019 Google LLC.    
   SPDX-License-Identifier: Apache-2.0 */
/**
 * A Job that refreshes the conference data in the repository (if the app is active) and
 * in the cache (if the app is not active).
 * 一个用来刷新资源库(当利用沉闷时)以及缓存(当利用不沉闷时)中的会议数据的工作。*/
class ConferenceDataWorker(
    ctx: Context,
    params: WorkerParameters,
    private val refreshEventDataUseCase: RefreshConferenceDataUseCase
) : CoroutineWorker(ctx, params) {override suspend fun doWork(): Result {Timber.i("ConferenceDataService triggering refresh conference data.")

        return try {refreshEventDataUseCase(Unit)
            Timber.d("ConferenceDataService finished successfully.")
            // Finishing indicating this job doesn't need to be rescheduled.
            // Finishing 意味着这个工作不须要被重新安排执行。Result.success()} catch (e: Exception) {Timber.e("ConferenceDataService failed. It will retry.")
            // Indicating worker should be retried
            // 意味着 Worker 应该被重试
            if (runAttemptCount < MAX_NUMBER_OF_RETRY) {Result.retry()
            } else {Result.failure()
            }
        }
    }
}

源码:ConferenceDataWorker.kt

如您所见,因为在 WorkerFactory 级别解决了参数的传递,因而在 Worker 类上没有 Dagger 注解。

这个参数是 Dagger 已知的,因而能够将其间接注入到咱们的自定义 WorkerFactory 中:

 

/* Copyright 2019 Google LLC.    
   SPDX-License-Identifier: Apache-2.0 */
class ConferenceDataWorkerFactory(private val refreshEventDataUseCase: RefreshConferenceDataUseCase) : WorkerFactory() {

    override fun createWorker(
        appContext: Context,
        workerClassName: String,
        workerParameters: WorkerParameters
    ): ListenableWorker? {return when (workerClassName) {
            ConferenceDataWorker::class.java.name ->
                ConferenceDataWorker(appContext, workerParameters, refreshEventDataUseCase)
            else ->
                // Return null, so that the base class can delegate to the default WorkerFactory.
                // 返回 null,这样基类就能够代理到默认的 WorkerFactory
                null
        }
    }
}

源码:ConferenceDataWorkerFactory.kt

这里依然没有 Dagger 注解 …… 起因是咱们应用了一个 DelegatingWorkerFactory 来协调那些单个的工厂(此时,咱们在 IOsched 中只有一个工厂,然而咱们以一种在须要时能够间接增加更多工厂的形式来构建它):

IoschedWorkerFactory.kt
 

/* Copyright 2019 Google LLC.    
   SPDX-License-Identifier: Apache-2.0 */
@Singleton
class IoschedWorkerFactory @Inject constructor(refreshConferenceDataUseCase: RefreshConferenceDataUseCase) : DelegatingWorkerFactory() {
    init {addFactory(ConferenceDataWorkerFactory(refreshConferenceDataUseCase))
    }
}

源码:IoschedWorkerFactory.kt

OK,这里就是应用 Dagger 向咱们的构造函数注入参数的中央。

在这个利用中,咱们决定应用按需初始化,并且应用 Dagger 注入所有配置:

/* Copyright 2019 Google LLC.    
   SPDX-License-Identifier: Apache-2.0 */
@Inject lateinit var workerConfiguration: Configuration

// 应用 DelegatingWorkerFactory 为 WorkManager 设置自定义配置
override fun getWorkManagerConfiguration(): Configuration {return workerConfiguration}

源码:MainApplication.kt

这使咱们能够为不同的构建类型注入不同的配置。尤其是,咱们为调试构建注入了日志级别设置为 DEBUG 的配置:

/* Copyright 2019 Google LLC.    
   SPDX-License-Identifier: Apache-2.0 */
@Singleton
@Provides
fun provideWorkManagerConfiguration(ioschedWorkerFactory: IoschedWorkerFactory): Configuration {return Configuration.Builder()
        .setMinimumLoggingLevel(android.util.Log.DEBUG)
        .setWorkerFactory(ioschedWorkerFactory)
        .build()}

源码:debugRelease SharedModule.kt

同时,公布版本应用默认调试级别来设置自定义工厂:

SharedModule.kt (在 shared/src/staging/j/c/g/s/a/i/shared/di/ 中)

/* Copyright 2019 Google LLC.    
   SPDX-License-Identifier: Apache-2.0 */
@Singleton
@Provides
fun provideWorkManagerConfiguration(ioschedWorkerFactory: IoschedWorkerFactory): Configuration {return Configuration.Builder()
        .setWorkerFactory(ioschedWorkerFactory)
        .build()}

源码:staging SharedModule.kt

当咱们首次获取 WorkManager 实例时,WorkManager 将按需初始化。当咱们收到 Firebase 音讯以获取新的时间表时,就会触发这个操作:

IoschedFirebaseMessagingService.kt

/* Copyright 2019 Google LLC.    
   SPDX-License-Identifier: Apache-2.0 */
private fun scheduleFetchEventData() {val constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .build()

    val conferenceDataWorker = OneTimeWorkRequestBuilder<ConferenceDataWorker>()
        .setInitialDelay(MINIMUM_LATENCY, TimeUnit.SECONDS)
        .setConstraints(constraints)
        .build()

    val operation = WorkManager.getInstance(this)
        .enqueueUniqueWork(
            uniqueConferenceDataWorker,
            ExistingWorkPolicy.KEEP,
            conferenceDataWorker)
        .result

    operation.addListener({ Timber.i("ConferenceDataWorker enqueued..") },
        {it.run() }
    )
}

源码:IoschedFirebaseMessagingService.kt

至此,咱们完结了摸索如何应用 Dagger 把参数注入到您的 Worker,同时也理解了如何将 WorkManager 集成到 iosched 这类的大型利用中。

总结

WorkManager 是一个性能非常弱小的库,它的默认配置曾经能够笼罩许多常见的应用场景。然而当您遇到某些状况时,诸如须要减少日志级别或须要把额定参数传入到您的 Worker 时,则须要一个自定义的配置。

心愿通过最近两篇文章所做的介绍,能让您对自定义 WorkManager 有一个良好的意识。如果您有任何疑难,能够在评论区中留言。

编码欢快!

退出移动版