Version
| Technology |
Version |
| Workmanager version |
0.9.0+3 |
| Workmanager Android |
0.9.0+2 |
| Xcode version |
|
| Swift version |
|
| iOS deployment target |
|
Describe the error
On Android when a WorkManager background job and the main app Activity start at the same time (e.g. process killed from recents, then both triggered simultaneously), the app crashes however the background job completes. Most probably BackgroundWorker companion FlutterLoader causes NullPointerException crash when app and WorkManager job start simultaneously:
java.lang.NullPointerException: Attempt to read from field 'java.lang.String io.flutter.embedding.engine.loader.FlutterApplicationInfo.flutterAssetsDir' on a null object reference in method
'java.lang.String io.flutter.embedding.engine.loader.FlutterLoader.findAppBundlePath()'
at FlutterActivityAndFragmentDelegate.addEntrypointOptions
at FlutterActivityAndFragmentDelegate.setUpFlutterEngine
Followed by warnings then work completion:
W/FlutterJNI: FlutterJNI.loadLibrary called more than once
W/FlutterJNI: FlutterJNI.init called more than once
I/WM-WorkerWrapper: Worker result SUCCESS for Work [ id=8b5a5aa4-4b14-4f99-b964-a1713ba64d42, tags={ dev.fluttercommunity.workmanager.BackgroundWorker } ]
D/WM-Processor: Processor 8b5a5aa4-4b14-4f99-b964-a1713ba64d42 executed; reschedule = false
D/WM-GreedyScheduler: Cancelling work ID 8b5a5aa4-4b14-4f99-b964-a1713ba64d42
D/WM-SystemJobScheduler: Scheduling work ID 8b5a5aa4-4b14-4f99-b964-a1713ba64d42Job ID 184
Probably this is whats happening: BackgroundWorker.companion holds private val flutterLoader = FlutterLoader(). The FlutterLoader() no-arg constructor internally calls FlutterInjector.instance(), which creates and locks the injector singleton as a side effect and produces a second FlutterLoader instance separate from FlutterInjector.instance().flutterLoader(). The worker initializes its own companion loader, but FlutterEngine(applicationContext) and the main app's FlutterActivityAndFragmentDelegate both use the injector's loader. In a race between the job and the app launch, the injector's loader can reach findAppBundlePath() before startInitialization() has been called on it, producing the NPE. Because ListenableWorker.startWork() runs on the main thread, the ordering of these two initializations is non-deterministic at process start.
Affected scenarios: App killed → WorkManager job fires → user opens app simultaneously or vice versa; any scenario where process starts fresh with a pending job and UI together.
Platform: Android only. Unfortunately, this issue is very difficult to reproduce because accurately timing the background job execution with app startup is nearly impossible. However, I have observed it multiple times on an emulator. The pattern was that the emulator had not been used for several days then started the emulator, and when I launched the app from Android Studio, the app appeared briefly for about a second, then immediately disappeared. Right after that, the background job executed logs for the same appended below. It is affecting multiple users on different devices in production.
Complete logs on Emulator:
D/FlutterJNI: Beginning load of flutter...
D/FlutterJNI: flutter (null) was loaded normally!
E/AndroidRuntime: FATAL EXCEPTION: main
E/AndroidRuntime: Process: com.abc.redacted, PID: 11233
E/AndroidRuntime: java.lang.NullPointerException: Attempt to read from field 'java.lang.String io.flutter.embedding.engine.loader.FlutterApplicationInfo.flutterAssetsDir' on a null object reference in method 'java.lang.String io.flutter.embedding.engine.loader.FlutterLoader.findAppBundlePath()'
E/AndroidRuntime: at io.flutter.embedding.engine.loader.FlutterLoader.findAppBundlePath(FlutterLoader.java:615)
E/AndroidRuntime: at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.addEntrypointOptions(FlutterActivityAndFragmentDelegate.java:244)
E/AndroidRuntime: at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.setUpFlutterEngine(FlutterActivityAndFragmentDelegate.java:340)
E/AndroidRuntime: at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.onAttach(FlutterActivityAndFragmentDelegate.java:197)
E/AndroidRuntime: at io.flutter.embedding.android.FlutterFragment.onAttach(FlutterFragment.java:1060)
E/AndroidRuntime: at androidx.fragment.app.Fragment.performAttach(Fragment.java:3075)
E/AndroidRuntime: at androidx.fragment.app.FragmentStateManager.attach(FragmentStateManager.java:510)
E/AndroidRuntime: at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:279)
E/AndroidRuntime: at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2214)
E/AndroidRuntime: at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2109)
E/AndroidRuntime: at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2052)
E/AndroidRuntime: at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3327)
E/AndroidRuntime: at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:3237)
E/AndroidRuntime: at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:263)
E/AndroidRuntime: at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:350)
E/AndroidRuntime: at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1696)
E/AndroidRuntime: at android.app.Activity.performStart(Activity.java:9198)
E/AndroidRuntime: at android.app.ActivityThread.handleStartActivity(ActivityThread.java:4305)
E/AndroidRuntime: at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:214)
E/AndroidRuntime: at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:194)
E/AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeLifecycleItem(TransactionExecutor.java:166)
E/AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:101)
E/AndroidRuntime: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:80)
E/AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2823)
E/AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:110)
E/AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:248)
E/AndroidRuntime: at android.os.Looper.loop(Looper.java:338)
E/AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:9067)
E/AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
E/AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:932)
D/FlutterJNI: Beginning load of flutter...
D/FlutterJNI: flutter (null) was loaded normally!
I/flutter : [IMPORTANT:flutter/shell/platform/android/android_context_gl_impeller.cc(104)] Using the Impeller rendering backend (OpenGLES).
W/FlutterJNI: FlutterJNI.loadLibrary called more than once
D/FlutterJNI: Beginning load of flutter...
D/FlutterJNI: flutter (null) was loaded normally!
W/FlutterJNI: FlutterJNI.prefetchDefaultFontManager called more than once
W/FlutterJNI: FlutterJNI.init called more than once
Syncing files to device sdk gphone64 x86 64...
I/DynamiteModule: Considering local module com.google.android.gms.cronet_dynamite:0 and remote module com.google.android.gms.cronet_dynamite:4311
I/DynamiteModule: Selected remote version of com.google.android.gms.cronet_dynamite, version >= 4311
V/DynamiteModule: Dynamite loader version >= 2, using loadModule2NoCrashUtils
W/om.abc.redacted: ClassLoaderContext classpath element checksum mismatch. expected=162677756, found=1402994959 (DLC[];PCL[base.apk*162677756]{PCL[/system/framework/org.apache.http.legacy.jar*4247870504]#PCL[/system/framework/com.android.location.provider.jar*1570284764]#PCL[/system/framework/com.android.media.remotedisplay.jar*487574312]#PCL[/system_ext/framework/androidx.window.extensions.jar*1030441313]#PCL[/system_ext/framework/androidx.window.sidecar.jar*3860983653]} | DLC[];PCL[/data/app/~~kIPQ58LhNa75MlW_2Pqrpw==/com.abc.redacted-aDuszTJdKUXcioUv_ZYoNQ==/base.apk*1402994959]{PCL[/system_ext/framework/androidx.window.extensions.jar*1030441313]#PCL[/system_ext/framework/androidx.window.sidecar.jar*3860983653]})
I/om.abc.redacted: AssetManager2(0x7590db640cf8) locale list changing from [] to [en-US]
I/WM-WorkerWrapper: Worker result SUCCESS for Work [ id=8b5a5aa4-4b14-4f99-b964-a1713ba64d42, tags={ dev.fluttercommunity.workmanager.BackgroundWorker } ]
D/WM-Processor: Processor 8b5a5aa4-4b14-4f99-b964-a1713ba64d42 executed; reschedule = false
D/WM-GreedyScheduler: Cancelling work ID 8b5a5aa4-4b14-4f99-b964-a1713ba64d42
D/WM-SystemJobScheduler: Scheduling work ID 8b5a5aa4-4b14-4f99-b964-a1713ba64d42Job ID 184
Logs from live devices on Flutter 3.41.9
Fatal Exception: java.lang.NullPointerException: Attempt to read from field 'java.lang.String bb.b.b' on a null object reference in method 'void wa.c.a(io.flutter.embedding.engine.b$b)'
at io.flutter.embedding.engine.loader.FlutterLoader.findAppBundlePath(FlutterLoader.java:615)
at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.addEntrypointOptions(FlutterActivityAndFragmentDelegate.java:244)
at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.setUpFlutterEngine(FlutterActivityAndFragmentDelegate.java:340)
at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.onAttach(FlutterActivityAndFragmentDelegate.java:197)
at io.flutter.embedding.android.FlutterFragment.onAttach(FlutterFragment.java:1058)
at androidx.fragment.app.Fragment.performAttach(Fragment.java:3075)
at androidx.fragment.app.FragmentStateManager.attach(FragmentStateManager.java:510)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:279)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2214)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2109)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2052)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3327)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:3237)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:263)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:350)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1455)
at android.app.Activity.performStart(Activity.java:8195)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3805)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2325)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:210)
at android.os.Looper.loop(Looper.java:299)
at android.app.ActivityThread.main(ActivityThread.java:8280)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:576)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1073)
Version
Describe the error
On Android when a WorkManager background job and the main app Activity start at the same time (e.g. process killed from recents, then both triggered simultaneously), the app crashes however the background job completes. Most probably BackgroundWorker companion FlutterLoader causes NullPointerException crash when app and WorkManager job start simultaneously:
Followed by warnings then work completion:
Probably this is whats happening:
BackgroundWorker.companion holds private val flutterLoader =FlutterLoader(). TheFlutterLoader()no-arg constructor internally callsFlutterInjector.instance(), which creates and locks the injector singleton as a side effect and produces a secondFlutterLoaderinstance separate fromFlutterInjector.instance().flutterLoader(). The worker initializes its own companion loader, butFlutterEngine(applicationContext)and the main app'sFlutterActivityAndFragmentDelegateboth use the injector's loader. In a race between the job and the app launch, the injector's loader can reachfindAppBundlePath()beforestartInitialization()has been called on it, producing the NPE. BecauseListenableWorker.startWork()runs on the main thread, the ordering of these two initializations is non-deterministic at process start.Affected scenarios: App killed → WorkManager job fires → user opens app simultaneously or vice versa; any scenario where process starts fresh with a pending job and UI together.
Platform: Android only. Unfortunately, this issue is very difficult to reproduce because accurately timing the background job execution with app startup is nearly impossible. However, I have observed it multiple times on an emulator. The pattern was that the emulator had not been used for several days then started the emulator, and when I launched the app from Android Studio, the app appeared briefly for about a second, then immediately disappeared. Right after that, the background job executed logs for the same appended below. It is affecting multiple users on different devices in production.
Complete logs on Emulator:
Logs from live devices on Flutter 3.41.9