Flutter:workmanager+home_widget在Debug/Profile正常,Release APK午夜任务失效
我开发了一款仅面向Android的Flutter待办APP,用home_widget实现了桌面组件,用户能直接在组件里查看任务列表并勾选完成任务。
原本设计每日午夜重置当日任务,用workmanager包实现(也试过android_alarm_manager_plus,结果一致)。这个功能在Debug和Profile模式下都正常,但构建Release APK提交Google Play内部测试,下载到Pixel 7(已确认没开省电模式)后,午夜重置任务完全不执行。
另外桌面组件勾选任务有时会延迟,甚至要打开APP才生效,这个我觉得是系统层面的问题,暂时不纠结,重点解决Workmanager的问题。
相关代码
main_prod.dart
void main() async { return mainGeneric('Prod Name', ProdFirebaseOptions.currentPlatform, Environment.prod); }
main_generic.dart
/// 用于通过Workmanager插件实现后台更新 @pragma('vm:entry-point') void workmanagerCallbackDispatcher() { Workmanager().executeTask((taskName, inputData) { if (taskName == 'widgetBackgroundUpdate') { try { return Future.wait<void>([ // 工具类静态Future<void>函数,重置任务;单独测试及debug/profile模式下正常 MidnightService.resetTasks(), ]).then((value) { return Future.value(true); }); } catch(err) { print(err.toString()); throw Exception(err); } } return Future.value(true); }); } void _startBackgroundUpdate() async { if (await MidnightService.shouldUpdateWorkManagerTasks()) { (await SharedPreferences.getInstance()).setInt('midnight_tasks_update_version', Constants.MIDNIGHT_TASKS_UPDATE_VERSION); await Workmanager().cancelAll(); DateTime now = DateTime.now(); int nowMillis = now.millisecondsSinceEpoch; int midnightTonightMillis = DateTime(now?.year ?? 0, now?.month ?? 0, (now?.day ?? 0) + 1).millisecondsSinceEpoch; int millisUntilMidnight = midnightTonightMillis - nowMillis; await Workmanager().registerPeriodicTask('midnightUpdate', 'widgetBackgroundUpdate', initialDelay: Duration(milliseconds: millisUntilMidnight), frequency: Duration(days: 1)); } } void mainGeneric(String appName, FirebaseOptions firebaseOptions, Environment environment) async { // 避免Flutter升级导致的错误。 WidgetsFlutterBinding.ensureInitialized(); Workmanager().initialize(workmanagerCallbackDispatcher, isInDebugMode: kDebugMode).then((_) => _startBackgroundUpdate()); ... // 如果是首次打开带有组件功能的APP。 HomeWidget.getWidgetData<String>('todays_tasks_string', defaultValue: '').then((todaysTasksString) async { if (todaysTasksString == '') { List<Task> todaysTasks = await Repositories().taskRepository.getFocusedTasks(); await HomeWidgetUtils.setTodaysTasks(todaysTasks); return true; } return false; }); Firebase.initializeApp( name: appName, options: firebaseOptions, ).then((_) async { ... }); HomeWidget.registerBackgroundCallback(homeWidgetBackgroundCallback); runApp(AppConfig( child: MyApp(), environment: environment, appTitle: appName, )); } // 处理桌面组件触发的后台任务 @pragma('vm:entry-point') Future<void> homeWidgetBackgroundCallback(Uri uri) async { if (uri.host.startsWith('completetask_')) { String todaysTasksString = await HomeWidgetUtils.updateTaskById(uri.host.split('_')[1], true); await HomeWidget.saveWidgetData<String>('todays_tasks_string', todaysTasksString); await HomeWidget.updateWidget(androidName: 'TodaysTasksWidgetProvider'); } }
midnight_service.dart
class MidnightService { ... static Future<bool> shouldUpdateWorkManagerTasks() async { try { final prefs = await SharedPreferences.getInstance(); int midnightTasksUpdateVersion = prefs.getInt('midnight_tasks_update_version'); return Constants.MIDNIGHT_TASKS_UPDATE_VERSION > midnightTasksUpdateVersion; } catch (e) { print(e); } return true; } }
可能的原因与解决方案
1. ProGuard/R8混淆导致入口被移除
Release模式下默认开启的代码混淆可能会把带有@pragma('vm:entry-point')标记的回调函数误删,导致Workmanager无法找到任务入口。
解决: 在android/app/proguard-rules.pro中添加规则,保留相关入口和类:
# 保留Workmanager相关组件 -keep class io.flutter.plugins.workmanager.** { *; } # 保留所有带vm:entry-point标记的方法 -keepclassmembers class * { @pragma('vm:entry-point') <methods>; } # 保留自定义的任务处理类 -keep class com.yourpackage.MidnightService { *; }
2. 周期性任务的系统限制
Android对周期性Workmanager任务有最小频率限制(Android 12+为15分钟),且initialDelay在部分版本中可能被系统忽略,加上跨月/跨年时日期计算可能存在隐性问题,导致任务不触发。
解决:
- 替换周期性任务为一次性任务:首次注册午夜触发的一次性任务,在任务执行完成后,重新计算次日午夜的延迟并注册新的一次性任务;
- 验证日期计算逻辑,比如月末31号+1的情况,确保
midnightTonightMillis计算正确。
3. Google Play签名后的配置问题
Google Play重新签名APK后,可能导致Workmanager的权限或组件注册失效。
解决:
- 确保
AndroidManifest.xml中正确配置Workmanager相关权限与组件:<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> <application ...> <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <meta-data android:name="androidx.work.WorkManagerInitializer" android:value="androidx.startup" tools:node="remove"/> </provider> </application> - 手动测试Play控制台下载的签名包,查看系统日志排查任务注册状态。
4. 后台任务节流限制
即使未开启省电模式,Android 12+对后台任务的节流依然严格,Workmanager任务可能被系统延迟或终止。
解决:
- 引导用户为APP关闭电池优化;
- 简化
MidnightService.resetTasks()中的操作,避免耗时过长被系统终止; - 若需要精确执行,可考虑结合
AlarmManager与前台服务(需额外权限适配)。
5. SharedPreferences版本判断逻辑漏洞
如果midnight_tasks_update_version未更新,APP后续启动时不会重新注册任务,若之前的任务被系统取消,就无法触发午夜重置。
解决:
- 添加任务存在性检查:每次APP启动时,验证Workmanager中是否存在
midnightUpdate任务,不存在则重新注册; - 调整版本号更新策略,确保每次需要重置任务时都能触发重新注册。
内容的提问来源于stack exchange,提问作者Anderillo

