You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 18:27:22