Jetpack之WorkManager(二)


前言

前面已经学习了WorkManager的基本使用Jetpack之WorkManager(一),接下来我们学习如何自定义Manager。


一、自定义WorkManager

在本小节,我们将要涉及如下内容:

  • 为什么需要自定义配置
  • 如何自定义配置
  • 什么是WorkerFactory并且为什么要自定义
  • DelegatingWorkerFactory是什么

1.1 开始

当使用WorkManager时,你需要定义一个Worker(无论是Worker、CoroutineWorker还是ListenableWorker)的子类。在某个时刻,WorkManager会通过WorkerFactory实例化你定义的 Worker。
默认的WorkerFactory创建的Worker需要两个参数:

  • Application’s Context
  • WorkerParameters
    如果你想要增加参数,那么只能通过自定义一个WorkerFactory实现。

默认的WorkerFactory使用反射技术实例化ListenableWorker。因此我们的类名不能混淆。

1.2 定义Configuration 和 WorkerFactory

WorkManager使用的是单例模式,因此之前在其初始化之前,改变其默认配置。在v1.0.0版本之后,如果初始化之后,再次调用初始化方法initialize(),app将会抛出响应的异常。但是到了v2.1.0版本之后,我们可以使用Configuration.Provider接口延迟初始化策略,这样我们就可以使用getInstance(context)获取相应实例。并且WorkManager将会使用你自定义的配置进行初始化。

1.2.1 可配置的参数

正如我上面所说,我们可以自定义其他参数来实例化一个Worker

  • Logging level
  • JobId range

我们定义上面两个参数的一样在于:Logging level 打印日志的级别。这对于我们查看、调试WorkManager是非常有帮助的;JobId range 是避免 当我们的app中有JobScheduer时,出现jobId冲突的问题。 针对这种case,在v2.4.0版本提供了一个检查类。

1.3 WorkManager的WorkerFactory

我们已经知道WorkManager有一个默认的WorkerFactory。它通过反射将我们在WorkRequest传入的worker类名 进行了实例化。

如果你创建一个WorkRequest ,然后使用其他的类名重构了你的app。WorkManager就找不到正确的类,从而抛出异常了。

你也许想要添加其他参数到Worker的构造方法中。如果你有一个Worker,想让他通过一个Retrofit的引用实现网络请求,那么我们应该像下面这样声明一个Worker:

class UpvoteStoryWorker(
  appContext: Context, 
  workerParams: WorkerParameters, 
  private val service: UpvoteStoryHttpApi) 
    : CoroutineWorker(appContext, workerParams) {
    
    

    override suspend fun doWork(): Result {
    
    

        return try {
    
    
            // Upvote story
            Result.success()
        } catch (e: Exception) {
    
    
            if (runAttemptCount < MAX_NUMBER_OF_RETRY) {
    
    
                Result.retry()
            } else {
    
    
                Result.failure()
            }
        }
    }
}

接下来,我们使用该Worker。运行后,我们会发下app报如下异常:

Caused by java.lang.NoSuchMethodException: <init> [class android.content.Context, class androidx.work.WorkerParameters]

因为我们自定义的Worker参数增加了, 默认的WorkerFactory无法实例化它。因此我们需要自定义一个WorkerFactory。我们需要做如下事情:

  • 关闭默认初始化
  • 自定义一个WorkerFactory
  • 自定义一个Configuration
  • 初始化WorkerManager

1.3.1 关闭默认初始化

在AndroidManifest.xml中逸出默认初始化器。该结点是在WorkManager中默认声明的。

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

1.3.2 实现自定义WorkerFactory

我们需要自定义一个WorkerFactory来实例化我们自定义的Worker

class MyWorkerFactory(private val service: UpvoteStoryHttpApi) : WorkerFactory() {
    
    

    override fun createWorker(
        appContext: Context,
        workerClassName: String,
        workerParameters: WorkerParameters
    ): ListenableWorker? {
    
    
	 // This only handles a single Worker, please don’t do this!!
	 // See below for a better way using DelegatingWorkerFactory
        return UpvoteStoryWorker(appContext, workerParameters, DesignerNewsService)

    }
}

1.3.3 创建WorkerConfiguration

接下来,我们需要将上面自定义的WorkerFactory注册到WorkerConfiguration中

class MyApplication : Application(), Configuration.Provider {
    
    
    override fun getWorkManagerConfiguration(): Configuration = 
        Configuration.Builder()
                     .setMinimumLoggingLevel(android.util.Log.DEBUG)
                     .setWorkerFactory(MyWorkerFactory(DesignerNewsService))
                     .build()
...
}

1.3.4 初始化WorkManager

如果你想要在应用中有多个Worker,请参考下一节实现。

1.4 DelegatingWorkerFactory

我们可以使用DelegatingWorkerFactory,将其设置到我们的WorkerFactory中,从而支持多个多个工厂。在这种情况下,如果一个WorkerFactory没有创建Worker,DelegatingFactory会去找到下一个WorkerFactory…

class MyWorkerFactory(private val service: DesignerNewsService) : WorkerFactory() {
    
    
    override fun createWorker(
        appContext: Context,
        workerClassName: String,
        workerParameters: WorkerParameters
    ): ListenableWorker? {
    
    

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

    }
}

我们的configuration将会这样使用:

class MyApplication : Application(), Configuration.Provider {
    
    

    override fun getWorkManagerConfiguration(): Configuration {
    
    
 	     val myWorkerFactory = DelegatingWorkingFactory()
     myWorkerFactory.addFactory(MyWorkerFactory(service))
     // Add here other factories that you may need in your application

            return Configuration.Builder()
                    .setMinimumLoggingLevel(android.util.Log.INFO)
                    .setWorkerFactory(myWorkerFactory)
                    .build()
    }
...
}

二、WorkManager和Dagger

2.1 为什么要使用Dagger

Dagger是google提供给Android开发者的一个依赖注入库。之前,我们已经了解如何自定义WorkerManager来传递多个参数。现在我们使用Dagger来注入这些参数。

2.2 使用Dagger注入参数到WorkerFactory

如果你使用Dagger管理你的依赖了,你需要把它集成到WorkerFactory上。如果你使用Dagger传递一个retrofit引用到你的Worker,那么Dagger需要将引用注入到WorkerFactory中来创建你的Worker。

现在我们假设Dagger提供了一个Retrofit Service实例的注入。这里不需要改变自定义工厂和WorkerManager的配置。仅仅是有一个被注入的参数:

@Singleton
class MyWorkerFactory @Inject constructor(
    service: DesignerNewsService
) : DelegatingWorkerFactory() {
    
    
    init {
    
    
        addFactory(myWorkerFactory(service))
 // Add here other factories that you may need in your application
    }
} 

在这个例子中,当我们初始化AppComponen后,我们使用AppComponent设置Dagger,开始先从Dagger到成员变量的赋值。

@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {
    
    

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

  fun inject(application: MyApplication)
}

接下来初始化AppComponent:

class MyApplication : Application(), Configuration.Provider {
    
    

    // other @Inject variables

    lateinit var appComponent: AppComponent

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

由于Dagger知道如何提供MyWorkerFactory的实例,你只需要使用@Inject获取实例,并使用它即可。

class MyApplication : Application(), Configuration.Provider {
    
    

    @Inject lateinit var myWorkerFactory: MyWorkerFactory 
    ...

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

2.3 相关链接

谷歌官网案例
翻译链接一
翻译链接二

猜你喜欢

转载自blog.csdn.net/dirksmaller/article/details/108826668