提供的代码片段是 Flutter 应用程序的一部分,该应用程序使用
flutter_background_service
包来运行后台任务。 onStart
函数是后台服务的入口点,负责定期收集和更新设备的位置。即使我关闭应用程序,这在 Android 上也能完美运行。但在 IOS 中,它仅在应用程序运行或暂停时才起作用。
Future<void> initializeBackgroundService() async {
final service = FlutterBackgroundService();
await service.configure(
androidConfiguration: AndroidConfiguration(
onStart: onStart,
autoStart: false,
autoStartOnBoot: false,
isForegroundMode: true,
initialNotificationTitle: "Tracking",
initialNotificationContent: 'Refreshing',
),
iosConfiguration: IosConfiguration(
autoStart: false,
onForeground: onStart,
),
);
}
Timer? timer;
@pragma('vm:entry-point')
void onStart(ServiceInstance service) async {
print("onStart called with $service");
try {
DartPluginRegistrant.ensureInitialized();
String __ = "";
String version = "";
String __ = "";
String url = "";
Position? position;
if (Platform.isAndroid) {
log("device is android");
if (service is AndroidServiceInstance) {
log("device is android service instance");
service.on('setAsForeground').listen((event) async {
//my codes
});
}
} else if (Platform.isIOS) {
service.on('setAsForeground').listen((event) async {
//my codes
});
}
if (Platform.isIOS) {
final NotificationService notificationService = NotificationService();
await notificationService.initializePlatformNotifications();
}
service.on('stopService').listen((event) async {
timer?.cancel();
await service.stopSelf();
log("Service stopped");
});
if (Platform.isAndroid) {
log("first notification android");
if (service is AndroidServiceInstance && await service.isForegroundService()) {
service.setForegroundNotificationInfo(
title: "Tracking",
content: "Last updated at ${DateFormat.jm().format(DateTime.now())}, ${DateFormat.yMd().format(DateTime.now())}",
);
}
} else if (Platform.isIOS) {
await NotificationService.showNotification(
id: 0,
title: "Tracking",
body: "Last updated at ${DateFormat.jm().format(DateTime.now())}, ${DateFormat.yMd().format(DateTime.now())}",
data: "",
);
}
timer?.cancel();
timer = Timer.periodic(const Duration(seconds: 10), (timer) async {
if (Platform.isAndroid) {
log("authcode for android $authcode");
if (service is AndroidServiceInstance) {
if (await service.isForegroundService()) {
Position? position;
bool isLocationEnabled = await Geolocator.isLocationServiceEnabled();
bool isPermissionGranted = await Geolocator.checkPermission() ==
LocationPermission.whileInUse ||
await Geolocator.checkPermission() == LocationPermission.always;
if (isLocationEnabled && isPermissionGranted) {
position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.bestForNavigation);
if (position.accuracy > 200) {
StreamSubscription<Position> positionStream =
Geolocator.getPositionStream(
locationSettings: AndroidSettings(
accuracy: LocationAccuracy.bestForNavigation,
distanceFilter: 0,
intervalDuration: const Duration(seconds: 1),
),
).listen((Position value) {
position = value;
});
await Future.delayed(const Duration(seconds: 5));
await positionStream.cancel();
}
log("authcode for android before call $authcode");
log("url for android before call $url");
//sending location from here
} else {
timer.cancel();
service.stopSelf();
}
}
}
} else if (Platform.isIOS) {
Position? position;
bool isLocationEnabled = await Geolocator.isLocationServiceEnabled();
bool isPermissionGranted = await Geolocator.checkPermission() ==
LocationPermission.whileInUse ||
await Geolocator.checkPermission() == LocationPermission.always;
if (isLocationEnabled && isPermissionGranted) {
position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.bestForNavigation);
if (position.accuracy > 200) {
StreamSubscription<Position> positionStream =
Geolocator.getPositionStream(
locationSettings: AppleSettings(
accuracy: LocationAccuracy.bestForNavigation,
distanceFilter: 0,
timeLimit: const Duration(seconds: 1),
activityType: ActivityType.otherNavigation,
),
).listen((Position value) {
position = value;
});
await Future.delayed(const Duration(seconds: 5));
await positionStream.cancel();
}
//sending location from here
} else {
timer.cancel();
service.stopSelf();
}
}
print('FLUTTER BACKGROUND SERVICE: ${DateTime.now()}');
});
} catch (e, stack) {
print(e);
print('stack: $stack');
}
}
我配置了 flutter_background_service 包并启用了 UIBackgroundModes 以在 Info.plist 文件中进行位置更新。
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>dev.flutter.background.refresh</string>
<string>myBackgroundTask</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
<string>fetch</string>
<string>remote-notification</string>
</array>
这是 Appdelegate.swift
import UIKit
import Flutter
import flutter_background_service_ios
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
SwiftFlutterBackgroundServicePlugin.taskIdentifier = "dev.flutter.background.refresh"
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
此包的 API 文档的最后一段看起来完美地描述了您所看到的症状:
当应用程序在 iOS 上处于后台(最小化)时服务终止
请记住,iOS 没有像 Android 那样的长期运行的服务功能。因此,当您的应用程序在后台时不可能保持其运行,因为操作系统很快就会挂起您的应用程序。目前,该插件提供了 onBackground 方法,该方法将通过 iOS 提供的后台 Fetch 功能定期执行。它不能快于 15 分钟,并且只能存活约 15-30 秒。