108 lines
3.2 KiB
Dart
108 lines
3.2 KiB
Dart
import 'dart:async';
|
||
import 'dart:io';
|
||
|
||
import 'package:autosos_flutter/service/heartbeat_service.dart';
|
||
import 'package:autosos_flutter/service/location_service.dart';
|
||
import 'package:flutter/foundation.dart';
|
||
import 'package:flutter_background_service/flutter_background_service.dart';
|
||
|
||
/// 后台保活服务(对应 Android JjSosLongService)
|
||
///
|
||
/// 启动后会在原生层持有一个前台服务(Foreground Service),
|
||
/// 即使 App 被系统回收,isolate 仍可继续执行心跳与位置上报。
|
||
class BackgroundService {
|
||
static final BackgroundService instance = BackgroundService._();
|
||
BackgroundService._();
|
||
|
||
static const String _notificationChannelId = 'autosos_bg_channel';
|
||
static const int _notificationId = 888;
|
||
|
||
final _service = FlutterBackgroundService();
|
||
|
||
/// 是否正在运行
|
||
Future<bool> isRunning() => _service.isRunning();
|
||
|
||
/// 初始化(在 main() 里调用一次)
|
||
Future<void> init() async {
|
||
if (!Platform.isAndroid && !Platform.isIOS) return;
|
||
|
||
await _service.configure(
|
||
androidConfiguration: AndroidConfiguration(
|
||
onStart: _onStart,
|
||
isForegroundMode: true,
|
||
autoStart: true,
|
||
autoStartOnBoot: true,
|
||
foregroundServiceNotificationId: _notificationId,
|
||
initialNotificationTitle: '啾啾救援',
|
||
initialNotificationContent: '正在保持在线...',
|
||
notificationChannelId: _notificationChannelId,
|
||
foregroundServiceTypes: const [
|
||
AndroidForegroundType.location,
|
||
AndroidForegroundType.dataSync,
|
||
],
|
||
),
|
||
iosConfiguration: IosConfiguration(
|
||
autoStart: true,
|
||
onForeground: _onStart,
|
||
onBackground: _onIosBackground,
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 启动后台服务
|
||
Future<void> start() async {
|
||
final running = await _service.isRunning();
|
||
if (!running) {
|
||
await _service.startService();
|
||
}
|
||
}
|
||
|
||
/// 停止后台服务
|
||
Future<void> stop() async {
|
||
_service.invoke('stopService');
|
||
}
|
||
|
||
/// Android 入口(独立 isolate)
|
||
@pragma('vm:entry-point')
|
||
static void _onStart(ServiceInstance service) async {
|
||
if (service is AndroidServiceInstance) {
|
||
service.on('setAsForeground').listen((event) {
|
||
service.setAsForegroundService();
|
||
});
|
||
service.on('setAsBackground').listen((event) {
|
||
service.setAsBackgroundService();
|
||
});
|
||
service.on('stopService').listen((event) async {
|
||
await service.stopSelf();
|
||
});
|
||
}
|
||
|
||
// 启动位置采集
|
||
try {
|
||
LocationService.instance.init();
|
||
LocationService.instance.start();
|
||
} catch (e) {
|
||
debugPrint('BG location start failed: $e');
|
||
}
|
||
|
||
// 启动心跳
|
||
HeartbeatService.instance.start();
|
||
|
||
// 持续保活:每 30 秒检查一次
|
||
Timer.periodic(const Duration(seconds: 30), (timer) async {
|
||
if (!HeartbeatService.instance.running) {
|
||
HeartbeatService.instance.start();
|
||
}
|
||
});
|
||
|
||
debugPrint('Background service started');
|
||
}
|
||
|
||
/// iOS 后台入口(必须尽快返回)
|
||
@pragma('vm:entry-point')
|
||
static bool _onIosBackground(ServiceInstance service) {
|
||
// iOS 不允许常驻后台任务,只能在限时窗口内上报
|
||
return true;
|
||
}
|
||
}
|