1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package androidx.work 17 18 import android.content.Context 19 import androidx.annotation.RestrictTo 20 21 /** 22 * A factory object that creates [ListenableWorker] instances. The factory is invoked every time a 23 * work runs. You can override the default implementation of this factory by manually initializing 24 * [WorkManager] (see [WorkManager.initialize] and specifying a new WorkerFactory in 25 * [Configuration.Builder.setWorkerFactory]. 26 */ 27 abstract class WorkerFactory { 28 /** 29 * Override this method to implement your custom worker-creation logic. Use 30 * [Configuration.Builder.setWorkerFactory] to use your custom class. 31 * 32 * Throwing an [Exception] here and no [ListenableWorker] will be created. If a [WorkerFactory] 33 * is unable to create an instance of the [ListenableWorker], it should return `null` so it can 34 * delegate to the default [WorkerFactory]. 35 * 36 * Returns a new instance of the specified `workerClassName` given the arguments. The returned 37 * worker must be a newly-created instance and must not have been previously returned or invoked 38 * by WorkManager. Otherwise, WorkManager will throw an [IllegalStateException]. 39 * 40 * @param appContext The application context 41 * @param workerClassName The class name of the worker to create 42 * @param workerParameters Parameters for worker initialization 43 * @return A new [ListenableWorker] instance of type `workerClassName`, or `null` if the worker 44 * could not be created 45 */ createWorkernull46 abstract fun createWorker( 47 appContext: Context, 48 workerClassName: String, 49 workerParameters: WorkerParameters 50 ): ListenableWorker? 51 52 /** 53 * Returns a new instance of the specified `workerClassName` given the arguments. If no worker 54 * is found, default reflection-based code will be used to instantiate the worker with the 55 * current ClassLoader. The returned worker should be a newly-created instance and must not have 56 * been previously returned or used by WorkManager. 57 * 58 * @param appContext The application context 59 * @param workerClassName The class name of the worker to create 60 * @param workerParameters Parameters for worker initialization 61 * @return A new [ListenableWorker] instance of type `workerClassName`, or `null` if the worker 62 * could not be created 63 * @throws IllegalStateException when `workerClassName` cannot be instantiated or the 64 * [WorkerFactory] returns an instance of the [ListenableWorker] which is used. 65 */ 66 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 67 fun createWorkerWithDefaultFallback( 68 appContext: Context, 69 workerClassName: String, 70 workerParameters: WorkerParameters 71 ): ListenableWorker { 72 fun getWorkerClass(workerClassName: String): Class<out ListenableWorker> { 73 return try { 74 Class.forName(workerClassName).asSubclass(ListenableWorker::class.java) 75 } catch (throwable: Throwable) { 76 Logger.get().error(TAG, "Invalid class: $workerClassName", throwable) 77 throw throwable 78 } 79 } 80 fun fallbackToReflection( 81 workerClassName: String, 82 workerParameters: WorkerParameters 83 ): ListenableWorker { 84 val clazz = getWorkerClass(workerClassName) 85 return try { 86 val constructor = 87 clazz.getDeclaredConstructor(Context::class.java, WorkerParameters::class.java) 88 constructor.newInstance(appContext, workerParameters) 89 } catch (e: Throwable) { 90 Logger.get().error(TAG, "Could not instantiate $workerClassName", e) 91 throw e 92 } 93 } 94 val worker = 95 createWorker(appContext, workerClassName, workerParameters) 96 ?: fallbackToReflection(workerClassName, workerParameters) 97 if (worker.isUsed) { 98 val message = 99 "WorkerFactory (${javaClass.name}) returned an instance of" + 100 " a ListenableWorker ($workerClassName) which has already been invoked. " + 101 "createWorker() must always return a new instance of a ListenableWorker." 102 throw IllegalStateException(message) 103 } 104 return worker 105 } 106 } 107 108 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 109 object DefaultWorkerFactory : WorkerFactory() { createWorkernull110 override fun createWorker( 111 appContext: Context, 112 workerClassName: String, 113 workerParameters: WorkerParameters 114 ) = null 115 } 116 117 private val TAG = Logger.tagWithPrefix("WorkerFactory") 118