Reputation: 1183
I'm migrating from the Android Priority JobQueue library to the AndroidX WorkManager
in my project, but it is built in a way that the Worker
should be something like this:
TaskSchedulerWorker.kt
class TaskSchedulerWorker(
private val workManager: WorkManager = WorkManager.getInstance()
) : TaskScheduler {
override fun execute(useCaseWrapper: UseCaseWrapper) {
workManager.beginUniqueWork(
useCaseWrapper.useCaseName,
ExistingWorkPolicy.APPEND,
OneTimeWorkRequest.from(UseCaseWrapperWorker::class.java)
).enqueue()
}
override fun onCancel(useCaseWrapper: UseCaseWrapper) {
workManager.cancelUniqueWork(useCaseWrapper.useCaseName)
}
}
UseCaseWrapperWorker.kt
class UseCaseWrapperWorker(
context: Context,
params: WorkerParams,
private val useCaseWrapper: UseCaseWrapper
) : Worker(context, params) {
override fun doWork(): Result {
return try {
useCaseWrapper.execute()
Result.success()
} catch (ex: Exception) {
ex.printStackTrace()
Result.failure()
}
}
}
The point is that if I try to run my app with those classes, I receive the next error:
2019-05-09 09:34:45.784 27260-27443/com.mobileapp.debug E/WM-WorkerFactory: Could not instantiate com.mobileapp.domain.jobqueue.UseCaseWrapperWorker
java.lang.NoSuchMethodException: com.mobileapp.domain.jobqueue.UseCaseWrapperWorker.<init> [class android.content.Context, class androidx.work.WorkerParameters]
at java.lang.Class.getConstructor0(Class.java:2328)
at java.lang.Class.getDeclaredConstructor(Class.java:2167)
at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:92)
at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:234)
at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:128)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
2019-05-09 09:34:45.784 27260-27443/com.mobileapp.debug E/WM-WorkerWrapper: Could not create Worker com.mobileapp.domain.jobqueue.UseCaseWrapperWorker
Because the Worker
constructor can not have more than the two default parameters (Context
and WorkerParameters
).
I am not able to figure out how to implement the UseCaseWrapperWorker
class with a constructor that receives the Context
, the WorkerParameters
and the UseCaseWrapper
, and invoke it from the TaskSchedulerWorker
with the OneTimeWorkRequest.from(UseCaseWrapperWorker::class.java)
instruction.
EDITED:
I've tried to implement a CustomWorkerFactory
and a CustomWorker
to pass them the UseCaseWrapper
. The problem is that I have to init the WorkManager
in the Application#onCreate
method (Android Developer Documentation), and I don't have access to the UseCaseWrapper
needed at this point.
TaskSchedulerWorker.kt
(the WorkManager
init should be in Application#onCreate
)
class TaskSchedulerWorker(private val appContext: Context) : TaskScheduler {
override fun execute(useCaseWrapper: UseCaseWrapper) {
val workerFactory = CustomWorkerFactory(useCaseWrapper)
val configuration = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
WorkManager.initialize(context, configuration)
WorkManager.getInstance().beginWork(
"uniqueWorkName",
ExistingWorkPolicy.APPEND,
OneTimeWorkRequest.from(CustomWorker::class.java)
)
}
}
CustomWorkerFactory.kt
class CustomWorkerFactory(private val useCaseWrapper: UseCaseWrapper) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
return CustomWorker(appContext, workerParameters, useCaseWrapper)
}
}
CustomWorker.kt
class CustomWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
private var useCaseWrapper: UseCaseWrapper? = null
constructor(
context: Context,
params: WorkerParameters,
useCaseWrapper: UseCaseWrapper
) : this(context, params) {
this.useCaseWrapper = useCaseWrapper
}
override fun doWork(): Result {
return useCaseWrapper?.let { useCaseWrapper ->
try {
useCaseWrapper.execute()
Result.success()
} catch (ex: Exception) {
Result.failure()
}
} ?: Result.failure()
}
}
The RuntimeException
thrown is:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mobileapp.debug/com.mobileapp.home.view.activity.HomeActivity}: java.lang.IllegalStateException: WorkManager is already initialized.
Did you try to initialize it manually without disabling WorkManagerInitializer? See WorkManager#initialize(Context, Configuration) or the class levelJavadoc for more information.
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3086)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3229)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1926)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:6981)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1445)
Caused by: java.lang.IllegalStateException: WorkManager is already initialized. Did you try to initialize it manually without disabling WorkManagerInitializer?
See WorkManager#initialize(Context, Configuration) or the class levelJavadoc for more information.
at androidx.work.impl.WorkManagerImpl.initialize(WorkManagerImpl.java:137)
at androidx.work.WorkManager.initialize(WorkManager.java:169)
at com.mobileapp.domain.worker.TaskSchedulerWorker.execute(TaskSchedulerWorker.kt:20)
at com.mobileapp.domain.usecase.UseCaseHandler.execute(UseCaseHandler.kt:41)
at com.mobileapp.domain.usecase.UseCaseCall.execute(UseCaseCall.kt:50)
at com.mobileapp.home.view.presenter.HomePresenter.getConfigurationJSON(HomePresenter.kt:107)
at com.mobileapp.home.view.activity.HomeActivity.initComponents(HomeActivity.kt:107)
at com.mobileapp.home.view.activity.HomeActivity.onCreate(HomeActivity.kt:67)
at android.app.Activity.performCreate(Activity.java:7326)
at android.app.Activity.performCreate(Activity.java:7317)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3066)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3229)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1926)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:6981)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1445)
Any suggestions?
Upvotes: 2
Views: 3642
Reputation: 215
you need to disable the default initializer
If you want to control the initialization process, you must disable the default initializer, then define your own custom configuration.
Disabling the default initializer
To provide your own configuration, you must first remove the default initializer. To do so, update AndroidManifest.xml using the merge rule tools:node="remove":
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
https://developer.android.com/topic/libraries/architecture/workmanager/advanced/custom-configuration
Upvotes: 1
Reputation: 563
You should use a custom WorkerFactory to initialize your Workers. See https://developer.android.com/reference/androidx/work/WorkerFactory
Upvotes: 1