当前位置: 首页 > news >正文

【Flutter】flutter_local_notifications并发下载任务通知实践

大概就是我在项目过程中并发下载任务弹通知遇到的一些问题,跟flutter_local_notifications关系也不大。

文章基本是AI生成,有些修改。

初始化

// 正确的初始化
import 'package:flutter_local_notifications/flutter_local_notifications.dart';final _notifications = FlutterLocalNotificationsPlugin();Future<void> initializeNotifications() async {// 引用 res/drawable/ic_notification.xmlconst AndroidInitializationSettings settings =AndroidInitializationSettings('ic_launcher'); //通知小图标await _notifications.initialize(InitializationSettings(android: settings));
}

这里有两个问题要注意,一是图标需要svg或者是png,其他的可能也行,不过webp的是显示不出来的,二是要在android目录下的,不是在flutter目录下的,我是放drawable里了,mipmap里应该也可能,没去试。以上都是经验之谈。

2. 实现下载进度条通知

展示下载进度是通知的一个核心应用场景,关键在于正确配置 AndroidNotificationDetails

  • 问题:进度条的 progressmaxProgress 参数只接受 int 类型,而下载进度是 double,直接转换会导致进度条“跳跃”。
  • 解决方案提升精度。将浮点数进度“放大”为整数。
    • 策略:将每个文件的总进度视为100。N个文件的最大进度 (maxProgress) 就是 N * 100。当前总进度 (progress) 则是所有文件 double 进度之和再乘以100。
double currentTotalProgress; // 例如: 2.5 (表示完成了2个文件,第3个下载了一半)
int totalFiles; // 例如: 5final int maxProgress = totalFiles * 100;
final int currentProgress = (currentTotalProgress * 100).toInt();final androidDetails = AndroidNotificationDetails('download_channel','下载进度',importance: Importance.low, // 使用 low importance 避免打扰priority: Priority.low,onlyAlertOnce: true,        // 只在第一次显示时提醒showProgress: true,progress: currentProgress,maxProgress: maxProgress,ongoing: true,              // 设为“进行中”,用户无法轻易划掉
);

其实onReceiveProgress的两个参数都是int。

await _dio.download(fileUrl,savePath,onReceiveProgress: (received, total) {......
}

之所以说下载进度是 double,是因为我要将进度返回给UI,所以单个文件的进度是received / total 。

多个文件的总进度可以将各个文件的received用map存起来,然后相加/下载的文件总数,因为total一般就是1。

3. 处理并发下载任务的通知

当应用支持同时进行多个下载任务时,如何管理通知以避免混乱是一个挑战。

  • 问题1:多个下载任务的进度更新到了同一个通知上。

    • 原因:并发任务共享了 Controller 中的成员变量作为通知ID和进度数据源,导致状态竞争和数据污染。
    • 解决方案状态隔离。为每个下载批次创建一个唯一的、自增的 notificationId,并将该批次的所有状态(如进度Map、节流计时器)都作为局部变量或封装在独立的状态对象中进行管理,杜绝共享。
  • 问题2:多个独立的通知因高频更新而在通知栏“上下跳动”。

    • 原因:每次调用 notifications.show() 更新通知,系统都会刷新其时间戳,而通知列表是按时间倒序排列的。
    • 解决方案固定排序依据。通过 AndroidNotificationDetailswhen 参数为每个通知批次设置一个固定的“伪时间戳”
      • 策略:在第一个任务启动时记录一个基准时间戳 baseTimestamp。对于每个任务,其伪时间戳为 baseTimestamp - notificationId。由于ID递增,这个伪时间戳是递减的,从而实现了通知按ID升序的固定排序
// 在 Controller 中
static int _notificationIdCounter = 0;
int? _baseTimestamp;// 在下载方法中
final int notificationId = ++_notificationIdCounter;
if (_baseTimestamp == null) {_baseTimestamp = DateTime.now().millisecondsSinceEpoch;
}
final int pseudoTimestamp = _baseTimestamp! - notificationId;// 在创建 AndroidNotificationDetails 时
final androidDetails = AndroidNotificationDetails(// ...when: pseudoTimestamp,  // 固定排序依据showWhen: false,        // 隐藏这个“假”的时间戳,UI更干净// ...
);// 所有下载任务结束后,重置 _baseTimestamp
if (all_tasks_are_finished) {_baseTimestamp = null;
}

就是每个通知都用跟这个通知绑定的局部变量,不要公用全局的。

ID不要用同一个,会直接更新替换前面的。

4. 性能优化:节流 (Throttling)

dio 等网络库的 onReceiveProgress 回调频率极高,直接在回调中更新通知会造成严重的性能问题和UI延迟。

  • 问题:高频调用 notifications.show() 导致系统 NotificationManager 负担过重,UI响应延迟。
  • 解决方案:在Dart层节流,控制通知的更新频率。
    • 策略:使用一个时间戳记录上次更新通知的时间。只有当距离上次更新超过一定间隔(如250-500毫秒)时,才真正调用 notifications.show()
DateTime? lastUpdateTime; // 批次的局部节流计时器// ... onReceiveProgress 回调 ...
final now = DateTime.now();
if (lastUpdateTime != null && now.difference(lastUpdateTime!).inMilliseconds < 500 &&received < total) { // 确保最后一次(完成时)总能更新return; // 时间太短,跳过本次更新
}
lastUpdateTime = now;// ... 执行真正的通知更新逻辑 ...

不节流的话,通知的下载进度条会比实际进度慢个百分之二三十,因为NotificationManager来不及处理消息队列。


文章转载自:

http://DRtpu9Y2.fjkkx.cn
http://0tHht1QC.fjkkx.cn
http://ot6ykGZZ.fjkkx.cn
http://pQWxOKaL.fjkkx.cn
http://rJj32CLt.fjkkx.cn
http://oc6fFlfe.fjkkx.cn
http://zJbmG1gm.fjkkx.cn
http://ZtqozkPG.fjkkx.cn
http://Zo8Nonfx.fjkkx.cn
http://51eBXnOO.fjkkx.cn
http://nOHxmmPw.fjkkx.cn
http://D2MYw71v.fjkkx.cn
http://8nz21PET.fjkkx.cn
http://VT7YN7Ap.fjkkx.cn
http://WLgf6Gxy.fjkkx.cn
http://oL7UhHTA.fjkkx.cn
http://kFItNjKO.fjkkx.cn
http://fpNG8wLO.fjkkx.cn
http://3HeDNGdF.fjkkx.cn
http://ndAN83rx.fjkkx.cn
http://N9bDacyY.fjkkx.cn
http://qKIIRsn9.fjkkx.cn
http://GlKgivQK.fjkkx.cn
http://L7BhPd6f.fjkkx.cn
http://vdJWmMV4.fjkkx.cn
http://0CIRuzWm.fjkkx.cn
http://bmETSde8.fjkkx.cn
http://Op3RW2p5.fjkkx.cn
http://HTNDt2qX.fjkkx.cn
http://0k2cnjxj.fjkkx.cn
http://www.dtcms.com/a/368200.html

相关文章:

  • 覆盖Transformer、GAN:掩码重建正在重塑时间序列领域!
  • 数据结构基础之队列:数组/链表
  • 数据可视化工具推荐:5款让图表制作轻松上手的神器
  • 【网安基础】--ip地址与子网掩码
  • spring AI 的简单使用
  • 【yolo】YOLOv8 训练模型参数与多机环境差异总结
  • 算法(keep learning)
  • C/C++中的可变参数 (Variadic Arguments)函数机制
  • 深度学习:CNN 模型训练中的学习率调整(基于 PyTorch)
  • Mattermost教程:用Docker搭建自己的开源Slack替代品 (团队聊天)
  • Electron 性能优化:内存管理和渲染效率
  • 数字隔离器,新能源汽车PTC中的“电气安全卫士”
  • 2025 汽车租赁大会:九识智能以“租赁+运力”革新城市智能配送
  • 云原生部署_Docker入门
  • javaweb(【概述和安装】【tomeat的使用】【servlet入门】).
  • 基于SpringBoot的社区智能垃圾管理系统【2026最新】
  • 基于飞算JavaAI的在线图书借阅平台设计实现
  • dbeaver工具连接inceptor星环数据库
  • Linux内核网络安全序列号生成机制解析
  • Buzz语音转文字:开源神器,高效记录会议
  • Docker 容器核心指令与数据库容器化实践
  • 自制扫地机器人 (五) Arduino 手机远程启停设计 —— 东方仙盟
  • docker 安装kafaka常用版本
  • Pytorch Yolov11 OBB 旋转框检测+window部署+推理封装 留贴记录
  • PyTorch 中.backward() 详解使用
  • conda配置pytorch虚拟环境
  • Conda环境隔离和PyCharm配置,完美同时运行PaddlePaddle和PyTorch
  • PyTorch训练循环详解:深入理解forward()、backward()和optimizer.step()
  • PyTorch 训练显存越跑越涨:隐式保留计算图导致 OOM
  • PyTorch图像数据转换为张量(Tensor)并进行归一化的标准操作