31

Having these dependencies:

dependencies {
    implementation "androidx.work:work-runtime:2.0.1"
    androidTestImplementation "androidx.work:work-testing:2.0.1"
}

When running this code for the second time:

Configuration config = new Configuration.Builder().build();
WorkManager.initialize(getApplicationContext(), config);

this.workManager = WorkManager.getInstance();

I get this error message:

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 level Javadoc for more information.

and it also throws a segmentation fault on the native side:

A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR),
fault addr 0x878 in tid 10892 (ova.workmanager),
pid 10892 (ova.workmanager)

This would be the documentation for WorkManager#initialize(Context, Configuration).


The intent is to prevent the crash during manual initialization (in order to change the log level). How to disable the WorkManagerInitializer? If possible, I do not want to use the static keyword.

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • Your question is missing a lot of details. Do you actually want to initialize the worker more than once? How does the first initialization even work, did you follow the documentation for initializing it properly? Where did you initialize the worker, which class and exactly where in this class's lifecycle? – ronginat Apr 26 '19 at 22:17
  • @ronginat compared to other questions, it is quite detailed. and you questions are probably all answered by the question already. of course I do not want to initialize it twice and there are no workers required to produce the crash. – Martin Zeitler Apr 26 '19 at 22:33
  • So far you answered to my first question so i know you don't want to do something impossible (you can't initialize the worker more than once). If your code is running multiple times and trying to initialize the worker, then it's probably not the right place for it. That's why i asked where you initialize the worker and how the first time is working without any change in your manifest (i'm guessing you didn't change the manifest as written in your answer when writing this question). – ronginat Apr 26 '19 at 22:38
  • @ronginat to be precise, it happens when scheduling one job and then later scheduling another; also when trying to cancel jobs. the tricky thing about it is, that `.getInstance()` is a `static` method and not an instance method. that answer is just an approach I've found, based upon the suggestion in the error message; still need to test that (the blog post is only a similar situation). – Martin Zeitler Apr 26 '19 at 22:46
  • Then the safest way will be initializing it only once at `Application#onCreate` as suggested in the [official guide](https://developer.android.com/topic/libraries/architecture/workmanager/advanced/custom-configuration#custom-config) (just below the code block). After that, use the static `getInstance` freely anywhere in your code because the `WorkManager` had already been initialized. – ronginat Apr 26 '19 at 22:52

6 Answers6

18

Since WorkManager 2.6, App Startup is used internally within WorkManager. To provide a custom initializer you need to remove the androidx.startup node.

If you don't use App Startup in your app, you can remove it completely.

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    tools:node="remove">
</provider>

Otherwise, remove only the WorkManagerInitializer node.

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">

    <!-- If you are using androidx.startup to initialize other components -->
    <meta-data
        android:name="androidx.work.WorkManagerInitializer"
        android:value="androidx.startup"
        tools:node="remove" />
</provider>

While using a version of WorkManager older than 2.6, remove workmanager-init instead:

<provider
    android:name="androidx.work.impl.WorkManagerInitializer"
    android:authorities="${applicationId}.workmanager-init"
    tools:node="remove" />
Andriy D.
  • 1,801
  • 12
  • 19
17

This is how to substitute provider androidx.work.impl.WorkManagerInitializer:

<application>
    ...

    <!-- disable default provider -->
    <provider
        android:name="androidx.work.impl.WorkManagerInitializer"
        android:authorities="${applicationId}.workmanager-init"
        android:exported="false"
        android:enabled="false"/>

    <!-- register custom provider -->
    <provider
        android:name=".CustomWorkManagerInitializer"
        android:authorities="${applicationId}.WorkManagerInit"/>

</application>

Source: Custom Work Manager initialization (in Kotlin).


Unless registering another provider, this gives a:

java.lang.IllegalStateException: WorkManager is not initialized properly. The most
likely cause is that you disabled WorkManagerInitializer in your manifest but forgot
to call WorkManager#initialize in your Application#onCreate or a ContentProvider.

And the ContentProvider registered in the src/debug/Manifest.xml:

public class WorkManagerInit extends ContentProvider {

    @Override
    public boolean onCreate() {
        if(getContext() != null) {
            Configuration config = new Configuration.Builder().build();
            WorkManager.initialize(getContext().getApplicationContext(), config);
        }
        return true;
    }
    ...
}
Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • it being a singleton was the actual problem - while `ContentProvider.onCreate()` and `Application.onCreate()` seem to be invoked once only, so one can only run the in there. In an `Activity` is might also work, when checking for `savedInstanceState != null`. – Martin Zeitler Apr 28 '19 at 09:46
9

WorkManager is a singleton, it needs to be configured before being used and you cannot change it's configuration without restarting the application.

A second call to initialize throws an exception to indicate that it cannot use that configuration if WorkManager has already been initialized. This was a design decision taken during the initial alpha releases to avoid to fail silently (workmanager-1.0.0-alpha11).

WorkManager v2.1 adds an on-demand way to initialize the library, so you can move the actual initialization out from the hot-start sequence of your app. However you still have the constraints that WorkManager can only be configured once.

With the new on-demand configuration, you've to add a Configuration.Provider interface to your application like here:

class MyApplication : Application(), Configuration.Provider {

   override fun getWorkManagerConfiguration(): Configuration =
       // provide custom configuration
       Configuration.Builder()
               .setMinimumLoggingLevel(android.util.Log.INFO)
               .setWorkerFactory(MyWorkerFactory())
               .build()
}

Then, when you need to use WorkManager, instead of using WorkManager#getInstance() (that is now deprecated), you should use the new:

WorkManager#getInstance(context)

In this case, the getInstance method, check if WorkManager it's already initialized or not. If it's not yet initialized, it retrieves the configuration using the getWorkManagerConfiguration() method and then it initializes WorkManager. In this way, you don't call anymore WorkManager#initialize() completely avoiding the problem.

The official documentation has been updated with these additional details.

If you think that WorkManager should allows to update it's configuration after it has been initialized, you should add a feature request to the library's issue tracker.

pfmaggi
  • 6,116
  • 22
  • 43
  • updating the configuration later on is not required, but it could use a method which tells the state of initialization... else once cannot initialize `OnDemand`, but only `OnCreate`. and without that method, debug/release builds might be the only way to switch providers. – Martin Zeitler Apr 28 '19 at 09:53
  • Let me know if the additional details on the new on-demand syntax do cover your question. In this case, you never call initialize and getInstance(context) takes care of initializing WorkManager if necessary – pfmaggi Apr 28 '19 at 13:14
4

for solving this problem disable default provider from manifest

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

and replace the following code

 <provider
        android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.workmanager-init"
        tools:node="remove" />
Foroogh Varmazyar
  • 1,057
  • 1
  • 14
  • 18
1

You can control when you called workManager

if(this.workManager == null){
   Configuration config = new Configuration.Builder().build();
   WorkManager.initialize(getApplicationContext(), config);
   this.workManager = WorkManager.getInstance();
}
Beyazid
  • 1,795
  • 1
  • 15
  • 28
0

If you want to initialize the WorkerManager you must put this in the onCreate of your custom Application class, so this only be called once. After that you can only get the instance when you need it without worries. It is not recommended to initialize the Worker Manager. Why do you need that?

Gerardo Suarez
  • 352
  • 2
  • 13