【Flutter】Flutter 进程保活 ① ( Flutter 后台保活底层机制 | flutter_background_service 插件开发流程 )
文章目录一、Flutter 后台保活底层机制1、Flutter 后台保活底层机制2、flutter_background_service 插件简介3、Dart Isolate 简介4、Isolate 核心组件 / 通信方式 / 独立内存二、使用 flutter_background_service 插件开发 Flutter 后台保活功能1、添加插件依赖2、添加 Android 权限 和 配置保活服务3、android/app/build.gradle 配置4、iOS 后台配置5、iOS 的 Info.plist 配置6、Flutter 代码参考7、Flutter 初始化后台服务参考文档 :Flutter 官方文档 : https://docs.flutter.dev/install/quick使用出现网络问题 , 参考 在中国网络环境下使用 Flutter 文档 ;使用 VS Code 开发 Flutter 环境安装 :https://docs.flutter.cn/install/with-vs-codeVS Code 安装 :https://code.visualstudio.com/docs/setup/setup-overview一、Flutter 后台保活底层机制1、Flutter 后台保活底层机制Flutter 后台保活 的 两大平台 底层机制差异 :Android 平台 :可以长期持续后台运行 , 核心依靠Foreground 前台服务 WakeLock 唤醒锁 电池白名单,独立 Dart 隔离区 (Isolate),退后台 / 锁屏也不会冻结线程, WebSocket/HTTP 持续收发网络包 ;iOS 平台 :无法无限后台常驻 ( 系统强制冻结 ) , 没有真正永久保活; 只能组合以下三种机制 :Audio 静音音频后台模式( 短期持续运行 , 易审核风险 )Background Fetch BGTaskScheduler 定时唤醒( 合规通用 )退后台十几秒后主线程直接挂起 , 只能靠定时唤醒做心跳 / 重连 ;2、flutter_background_service 插件简介flutter_background_service 插件是 Flutter 生态跨平台后台常驻服务插件, 核心能力 :应用退后台、甚至完全划掉关闭后 , 依然独立运行 Dart 代码 , 不依赖前台页面 Isolate ;Pub 地址 :https://pub.dev/packages/flutter_background_service支持 :Android iOS、空安全、Dart3、Flutter 3.0 , 最新稳定版 5.x授权 :MIT 开源flutter_background_service 插件底层封装原生系统后台能力,单独创建独立 Dart Isolate 执行后台逻辑 , 与 UI 主线程隔离,通过自定义消息通道实现「前台页面 ↔ 后台服务」双向通信 :Android :封装 Foreground Service ( 前台服务 ),常驻通知栏保活 ;iOS :封装系统 BGTaskScheduler Background Fetch,无真正无限后台 , 仅短周期定时任务Pub ;3、Dart Isolate 简介Isolate 直译 隔离区 , 是 Dart 提供的并发执行单元 :每个 Isolate独立内存、独立 GC、独立事件循环 ;隔离含义 :一个 Isolate 崩溃不会干掉其他 Isolate ;Isolate 之间唯一通信方式 :Port 消息传递( 复制传递数据 , 不共享引用 ) ;Flutter App默认自带 1 个 Main Isolate( UI、渲染、业务逻辑都在这里 ) ;Dart 是单线程语言 , 所有默认代码跑在 Main Isolate ( 主线程 ),只有一条事件循环 ( Event Loop ), 不存在传统多线程共享内存 , Main Isolate ( Flutter UI 主线程 ) 特殊限制 :渲染、Build、Widget、Context 只能在 Main Isolate 使用 ;子 Isolate 不能调用 Flutter 渲染相关 API , 不能更新 UI ;如果子 Isolate 需要更新页面 :只能通过 SendPort 发数据给主线程 , 主线 setState 更新界面 ;插件 MethodChannel、原生通道默认绑定主线程 ,子 Isolate 直接调用会报错 ;Dart Isolate 与 操作系统 Thread 的 关键区别 :操作系统 Thread :多线程共享堆内存 , 靠锁同步 ;Dart Isolate :内存完全隔离 , 每个 Isolate 拥有独立堆、独立 GC、独立事件循环 , 互相不能直接访问变量 ;特性Dart Isolate操作系统 Thread内存模型完全独立堆 , 不共享共享进程堆内存同步机制仅 Port 消息传递锁、信号量、共享变量崩溃影响单个隔离区崩溃互不影响一个线程崩溃整个进程退出GC每个 Isolate 独立 GC进程全局 GCFlutter UI仅 Main Isolate 可操作 UI任意线程都不能操作 UI4、Isolate 核心组件 / 通信方式 / 独立内存Isolate 核心组件 是 Event Loop ( 事件循环 ) :每个 Isolate 独有一套事件循环 , 分两个队列:Microtask Queue ( 微任务 ) :优先级更高 , 先执行完所有微任务再处理事件队列 ;Event Queue ( 事件队列 ) :Future、Timer、IO、手势、网络、消息端口都放这里 ;主线程卡顿本质 :耗时同步代码阻塞 Event Loop , UI 渲染无法执行 → 界面卡死 ;Isolate 就是用来把耗时计算丢到另一个隔离循环 , 不阻塞 UI ;Isolate 通信方式 :Isolate 无法直接访问对方变量 ,依靠 SendPort / ReceivePort 端口传消息 :ReceivePort:当前 Isolate 接收消息的端口 ;SendPort:发送消息到另一个 Isolate 的句柄 ;数据传递规则 :基础类型 ( int、String、bool、List、Map ) :深拷贝复制 ;无法传递 :函数、Widget、BuildContext、全局状态、文件句柄、网络连接 ;特殊 :SendPort 可以跨 Isolate 传递 , 作为通信桥梁 ;每个 Isolate 隔离区 都有 自己 独立堆内存 和 CG 垃圾回收器 :A Isolate 创建的对象 , B Isolate 完全不可见 ;各自都有一套 GC , 互不干扰 ;内存开销 :每个新建 Isolate 会占用额外内存 , 不宜无限制创建 ;二、使用 flutter_background_service 插件开发 Flutter 后台保活功能1、添加插件依赖在 pubspec.yaml 配置文件中 , 添加 flutter_background_service 依赖 ;dependencies: flutter_background_service: ^5.0.5配置完毕后 , 执行flutter pub get命令 , 安装插件 ;2、添加 Android 权限 和 配置保活服务android/app/src/main/AndroidManifest.xml 中 配置 以下权限 和 BackgroundService 保活服务 :manifestxmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:toolshttp://schemas.android.com/tools!-- 前台服务权限 --uses-permissionandroid:nameandroid.permission.FOREGROUND_SERVICE/uses-permissionandroid:nameandroid.permission.FOREGROUND_SERVICE_DATA_SYNC/uses-permissionandroid:nameandroid.permission.FOREGROUND_SERVICE_LOCATION/!-- 前台服务通知权限 --uses-permissionandroid:nameandroid.permission.POST_NOTIFICATIONS/applicationandroid:name${applicationName}android:iconmipmap/ic_launcher!-- 后台保活服务 --serviceandroid:nameid.flutter.flutter_background_service.BackgroundServiceandroid:foregroundServiceTypelocationandroid:exportedfalseandroid:stopWithTaskfalseandroid:enabledtruetools:replaceandroid:exported//application/manifest3、android/app/build.gradle 配置android/app/build.gradle 配置 最低 API 版本 :注意 最低版本 要求 21 版本 ;defaultConfig{minSdkVersion21targetSdkVersion34}4、iOS 后台配置调用open ios/Runner.xcworkspace命令 , 使用 Xcode 打开 Flutter 的 iOS 项目 ;选中 Runner Target → Signing Capabilities ;点击 Capability , 搜索添加 Background Modes ;勾选两项核心模式 :Background fetch ( 周期唤醒 )Background processing ( 支持稍长数据处理 )额外场景补充 :后台 GPS 轨迹 : 追加勾选 Location updates后台音乐播放 : 追加勾选 Audio, AirPlay5、iOS 的 Info.plist 配置在 iOs/Runner/Info.plist 中 配置如下 键值对 :keyUIBackgroundModes/keyarraystringfetch/stringstringprocessing/string/array!-- 后台刷新描述 , 审核必填 --keyBGTaskSchedulerPermittedIdentifiers/keyarraystringcom.xxx.app.syncservice/string/arraykeyNSAppTransportSecurity/keydictkeyNSAllowsArbitraryLoads/keytrue//dict6、Flutter 代码参考Flutter 代码参考 :importdart:async;importdart:ui;importpackage:flutter_background_service/flutter_background_service.dart;/// 后台独立进程启动顶层回调方法/// 强制要求顶层独立函数 , 不能封装在类内部/// pragma(vm:entry-point) 注解用于AOT编译 , 让Native引擎能够找到该后台入口/// 该方法运行在全新独立Dart隔离区 , 与前端UI线程内存完全隔离pragma(vm:entry-point)voidisolateTaskBootEntry(ServiceInstanceservice){// 后台隔离区初始化注册Flutter底层引擎// 增加try-catch捕获插件底层潜在初始化异常 , 避免后台进程直接崩溃退出try{DartPluginRegistrant.ensureInitialized();}catch(e){print(多进程管理工具 后台隔离引擎初始化捕获可忽略异常 :$e);}print(多进程管理工具 后台独立进程已触发启动回调);// 监听前端发送的进程停止指令 , 收到后主动销毁后台服务service.on(stop).listen((event){service.stopSelf();print(多进程管理工具 收到前端停止指令 , 后台独立进程已销毁);});// 后台常驻循环定时器 , 每5秒打印运行日志 , 用于验证进程存活状态Timer.periodic(constDuration(seconds:5),(timer){print(多进程管理工具 后台独立进程持续正常运行中...);});print(多进程管理工具 后台进程启动回调执行完毕 , 常驻任务已开启);}/// iOS系统定时唤醒后台任务顶层方法/// iOS系统无法永久常驻后台 , 依靠系统定时调度触发该方法执行轻量任务/// 必须顶层函数并添加vm入口注解 , 否则系统调度时无法执行pragma(vm:entry-point)FutureboolisolateIosBackgroundTimerTask(ServiceInstanceservice)async{// iOS后台唤醒后 , 必须重新初始化Flutter引擎注册器DartPluginRegistrant.ensureInitialized();// iOS后台唤醒后启动定时日志循环 , 校验唤醒后进程可正常执行代码Timer.periodic(constDuration(seconds:5),(timer){print(iOS定时后台任务 唤醒后任务持续执行中...);});print(多进程管理工具 iOS系统定时唤醒任务已执行);returntrue;}/// 跨平台后台进程统一管理工具类/// 底层依赖 flutter_background_service 插件实现App后台保活能力/// 同时兼容Android、iOS两大移动端系统 , 两套系统差异化配置统一封装/// 采用饿汉单例设计模式 , 全局仅存在唯一管理实例 , 避免多实例冲突classAppBackgroundTaskManager{/// 饿汉单例核心静态常量/// 程序加载阶段直接创建唯一实例 , 全局任意位置调用均使用同一个对象staticfinalAppBackgroundTaskManagerinstanceAppBackgroundTaskManager._privateConstructor();/// 私有内部构造方法/// 私有化后外部无法通过 new/构造函数手动创建新实例 , 保证单例唯一性AppBackgroundTaskManager._privateConstructor(){print(多进程管理工具 全局单例对象完成初始化创建);}/// 对外暴露工厂构造函数/// 外部代码直接调用类名()即可获取全局唯一实例 , 使用方式更简洁factoryAppBackgroundTaskManager()instance;/// flutter后台服务底层原生实例对象finalFlutterBackgroundService_backgroundServiceCoreFlutterBackgroundService();/// 标记后台进程当前运行状态bool _serviceRunningFlagfalse;/// 只读对外状态获取器/// 外部仅能读取运行标记 , 无法直接修改内部状态变量boolgetisServiceRunning_serviceRunningFlag;/// 后台进程全局初始化配置方法/// 统一配置Android前台服务参数、iOS后台调度参数/// 程序启动阶段仅执行一次 , 不可重复调用configure方法FuturevoidinitGlobalBackgroundConfig()async{await_backgroundServiceCore.configure(androidConfiguration:AndroidConfiguration(// Android进程启动绑定顶层隔离任务入口方法onStart:isolateTaskBootEntry,// App启动自动拉起后台服务autoStart:true,// 开启前台服务模式 , Android保活核心开关 , 状态栏常驻通知防止系统休眠冻结进程isForegroundMode:true,// 使用插件内置默认通知渠道 , 无需开发者手动在原生层创建渠道资源// 若自定义渠道ID , 必须同步在Android原生代码创建对应通知渠道 , 否则前台通知不展示// 前台服务通知唯一标识ID , 避免与App其他通知冲突foregroundServiceNotificationId:888,// 状态栏前台通知标题文本initialNotificationTitle:应用后台同步服务,// 状态栏前台通知详情描述 , 告知用户后台服务作用initialNotificationContent:应用后台同步服务正在持续运行 , 保障数据实时同步,),iosConfiguration:IosConfiguration(// App未完全退出、退至后台短时运行时执行的隔离任务onForeground:isolateTaskBootEntry,// iOS系统定时周期唤醒执行的轻量化后台任务onBackground:isolateIosBackgroundTimerTask,// App启动自动初始化后台调度配置autoStart:true,),);print(多进程管理工具 跨平台后台进程全局配置已完成);}/// 启动后台独立保活进程主方法/// 调用底层插件接口拉起隔离后台进程 , 更新内部运行状态标记FuturevoidlaunchBackgroundTask()async{// 已删除电池优化、厂商自启动引导相关全部逻辑 , 无额外前置系统设置操作finalbool launchResultawait_backgroundServiceCore.startService();_serviceRunningFlaglaunchResult;if(launchResult){print(多进程管理工具 后台独立保活进程启动成功);}else{print(多进程管理工具 后台独立保活进程启动执行失败);}}/// 主动停止后台保活进程方法/// 向前台隔离进程发送停止指令 , 修改运行状态标记FuturevoidshutdownBackgroundTask()async{_backgroundServiceCore.invoke(stop);_serviceRunningFlagfalse;print(多进程管理工具 后台独立保活进程停止指令已下发);}}7、Flutter 初始化后台服务初始化后台服务入口 :// 第一步 : 全局初始化跨平台后台保活独立进程配置awaitBgTaskGlobalManager().setupCrossPlatformBgConfig();// 第二步 : 执行启动后台常驻保活进程awaitBgTaskGlobalManager().startGlobalBgTask();