Background isolate throws `No top-level getter 'xxx' declared` when app is obfuscated
Opened this issue · 5 comments
When app is obfuscated background isolates run by flutter_workmanager
throw Dart Unhandled Exception: NoSuchMethodError: No top-level getter 'xxx' declared
in Flutter 2.10 and 3. However it works fine without obfuscation in both Flutter 2.10 and 3. It also works with obfuscation in Flutter 2.8.
Steps to reproduce:
- Add
workmanager: ^0.5.0
dependency inpubspec.yaml
- Create a new flutter App with this code in
main.dart
Code
import 'package:flutter/material.dart';
import 'package:workmanager/workmanager.dart';
void bgCallback() {
Workmanager().executeTask((task, inputData) async {
WidgetsFlutterBinding.ensureInitialized();
debugPrint('bgCallback | $task was executed. inputData = $inputData');
return Future.value(true);
});
}
void main() {
WidgetsFlutterBinding.ensureInitialized();
Workmanager().initialize(bgCallback, isInDebugMode: true);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
onPressed: scheduleJob,
child: const Text('Run BG tasks'),
),
);
}
Future scheduleJob() async {
await Workmanager().registerOneOffTask(
'aTestJob',
'aTestJob',
existingWorkPolicy: ExistingWorkPolicy.replace,
);
}
}
-
Build the app with obfuscation using:
flutter build apk --obfuscate --split-debug-info=./build/app/outputs/obfuscation_symbols
-
Install
app-release.apk
on a physical Android device or a 64bit emulator -
Run the App and press
Run BG tasks
, this should schedule a one time Android workmanager job, which should ideally runbgCallback
, you will see in the notification that workmanager is trying to run the callback
-
Expected result is to run
bgCallback
, but you can see in logcat, it will throwDart Unhandled Exception: NoSuchMethodError: No top-level getter 'xxx' declared.
Complete Logcat
2022-06-04 23:30:58.669 6711-6830/com.example.untitled1 E/flutter: [ERROR:flutter/shell/common/shell.cc(93)] Dart Unhandled Exception: NoSuchMethodError: No top-level getter 'sKb' declared.
Receiver: top-level
Tried calling: sKb, stack trace: Warning: This VM has been configured to produce stack traces that violate the Dart standard.
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
pid: 6711, tid: 6830, name Unknown
build_id: '9771c8715b0df74d9646fccb467f9b57'
isolate_dso_base: 78dc80f0b000, vm_dso_base: 78dc80f0b000
isolate_instructions: 78dc80fa6e70, vm_instructions: 78dc80fa3000
#00 abs 000078dc80fc7712 virt 00000000000bc712 _kDartIsolateSnapshotInstructions+0x208a2
2022-06-04 23:30:58.669 6711-6830/com.example.untitled1 E/flutter: [ERROR:flutter/runtime/dart_isolate.cc(668)] Could not resolve main entrypoint function.
2022-06-04 23:30:58.669 6711-6830/com.example.untitled1 E/flutter: [ERROR:flutter/runtime/dart_isolate.cc(167)] Could not run the run main Dart entrypoint.
2022-06-04 23:30:58.669 6711-6830/com.example.untitled1 E/flutter: [ERROR:flutter/runtime/runtime_controller.cc(382)] Could not create root isolate.
2022-06-04 23:30:58.669 6711-6830/com.example.untitled1 E/flutter: [ERROR:flutter/shell/common/shell.cc(600)] Could not launch engine with configuration.
Environment:
- Dart SDK version: 2.17.1 (stable) (Tue May 17 17:58:21 2022 +0000) on "windows_x64"
- Flutter Channel stable 3.0.1
- OS: Windows 10
- Android device and Android 11 emulator
Had the same problem. Turn off obfuscation The problem still exists.
This is working as intended and ultimately has nothing to do with with obfuscation. If you try your code on master channel of Flutter you will discover that it does not work either even without obfuscation.
As it stands now to fix things you need to annotate bgCallback
with @pragma('vm:entry-point')
:
@pragma('vm:entry-point')
void bgCallback() {
// ...
}
The underlying problem is that PluginUtilities.getCallbackHandle/getCallbackFromHandle
assumes that you can lookup arbitrary top-level static functions by name, which is not true and only ever worked by accident rather than design.
We have a problem on our hands - plugins that use PluginUtilities
without telling users to annotate static functions with entry-point pragma are essentially broken. We can opt-in to fix this by always retaining top-level functions in lookup dictionaries if they were ever torn-off, but I'd say this is rather suboptimal.
/cc @bkonyi @rmacnak-google @goderbauer @chinmaygarde @Hixie
Thank you very much mraleph it works with @pragma('vm:entry-point')
. Am happy to close the issue as long as this is documented somewhere for plugin authors.