使用WorkManager调度Flutter Downloader后台任务执行失败问题排查
问题描述
我希望每天在特定时间利用Flutter Downloader包完成一批文件的下载任务,因此使用WorkManager来调度并运行该任务,但任务并未执行。目前我将任务设置为每小时检查一次调度下载,仍然没有效果。
WorkManager调度代码
void callbackDispatcher() { Workmanager().executeTask((taskName, inputData) async { switch (taskName) { case fetchBackground: //check the time and download the movie // if (DateTime.now().hour == 23) { //TODO change to the correct call to sharedPrefrences ScheduledDownload(); //then run the download task. // } break; } return Future.value(true); }); } void main() async { //initialize WorkManager await Workmanager().initialize(callbackDispatcher, isInDebugMode: true); //register the periodic background task await Workmanager().registerPeriodicTask( "1", fetchBackground, frequency: const Duration(hours: 1), ); }
Flutter Downloader下载任务类
class DownloadCallBackClass { @pragma('vm:entry-point') static void downloadCallBack( String id, DownloadTaskStatus status, int progress) { final SendPort? send = IsolateNameServer.lookupPortByName('downloader_send_port'); send!.send([id, status, progress]); } } class ScheduledDownload { List<DownloadTask>? _downloadTasks; final ReceivePort _receivePort = ReceivePort(); void _loadTasks() async { _downloadTasks = await FlutterDownloader.loadTasks(); if (_downloadTasks!.length > 0) { print ("the number of download task is greater than 1"); _downloadTasks!.forEach((element) async { if (element.status == DownloadTaskStatus.paused) { await FlutterDownloader.resume(taskId: element.taskId); } else if (element.status == DownloadTaskStatus.failed) { String _url = element.url; await FlutterDownloader.remove(taskId: element.taskId); await FlutterDownloader.enqueue( url: _url, savedDir: '/storage/emulated/0/Download'); } }); } print ("the number of task is less than 1"); } void _initializeFlutterDownloader() async { await FlutterDownloader.initialize( debug: true, ignoreSsl: true, ).then((value) { IsolateNameServer.registerPortWithName( _receivePort.sendPort, 'downloader_send_port'); _receivePort.listen((dynamic data) { String id = data[0]; DownloadTaskStatus status = data[1]; int progress = data[2]; // setState(() { // _progress = progress; // _status = status; // }); }); FlutterDownloader.registerCallback( DownloadCallBackClass.downloadCallBack); _loadTasks(); }); } ScheduledDownload() { //initialize flutter downloader print("in scheduled download"); _initializeFlutterDownloader(); } }
解决方案
1. 修复WorkManager任务标识与入口逻辑
- 定义全局常量任务名,避免未定义标识符问题:
const fetchBackground = "fetch_background_task"; callbackDispatcher必须是顶级函数,后台任务中不能依赖Flutter主Isolate的通信机制,直接调用异步下载逻辑并等待完成。
2. 调整Flutter Downloader在后台的初始化逻辑
- 移除
IsolateNameServer和ReceivePort相关代码,后台隔离区无法与主Isolate通信,这部分逻辑无效。 - 下载回调函数保留
@pragma('vm:entry-point')注解,改为直接记录日志或本地存储状态,不要通过SendPort发送消息:class DownloadCallBackClass { @pragma('vm:entry-point') static void downloadCallBack( String id, DownloadTaskStatus status, int progress) { print("Download task $id: $status, progress: $progress"); } }
3. 修正异步任务执行逻辑
- 用
for循环替代forEach,确保所有异步操作等待完成:Future<void> runScheduledDownload() async { await FlutterDownloader.initialize(debug: true, ignoreSsl: true); FlutterDownloader.registerCallback(DownloadCallBackClass.downloadCallBack); final tasks = await FlutterDownloader.loadTasks(); if (tasks != null && tasks.isNotEmpty) { for (final task in tasks) { if (task.status == DownloadTaskStatus.paused) { await FlutterDownloader.resume(taskId: task.taskId); } else if (task.status == DownloadTaskStatus.failed) { await FlutterDownloader.remove(taskId: task.taskId); await FlutterDownloader.enqueue( url: task.url, savedDir: '/storage/emulated/0/Download', ); } } } } - 在
callbackDispatcher中调用该异步函数并等待:void callbackDispatcher() { Workmanager().executeTask((taskName, inputData) async { if (taskName == fetchBackground) { if (DateTime.now().hour == 23) { await runScheduledDownload(); } } return Future.value(true); }); }
4. 配置Android平台权限与WorkManager
- 在
AndroidManifest.xml中添加WorkManager初始化配置:<provider android:name="androidx.work.impl.WorkManagerInitializer" android:authorities="${applicationId}.workmanager-init" android:enabled="false" android:exported="false" /> - 添加必要权限:
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> - 针对Android 12+,声明前台服务类型:
<service android:name="be.tramckrijte.workmanager.ForegroundService" android:foregroundServiceType="dataSync" /> - 运行时请求忽略电池优化权限,避免系统限制后台任务:
await AndroidIntent( action: 'android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS', data: 'package:${(await PackageInfo.fromPlatform()).packageName}', ).launch();
5. 调试与验证
- 查看Logcat过滤
WorkManager和FlutterDownloader日志,确认任务是否被调度、是否有报错。 - 使用
adb shell dumpsys jobscheduler命令查看系统中WorkManager任务的状态,检查是否有任务被阻塞。
内容的提问来源于stack exchange,提问作者mbakabilal
相关产品推荐
相关产品推荐

