Flutter中Workmanager后台无法通过Geolocator获取定位的解决方法
后台使用Workmanager+Geolocator获取定位报错解决方法
我用Workmanager在后台通过Geolocator获取定位,前台运行无异常,但后台触发任务时出现任务被取消的报错,相关代码与报错信息如下:
实现代码
import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:workmanager/workmanager.dart'; late LocationSettings locationSettings; const fetchBackground = "fetchBackground"; @pragma('vm:entry-point') void callbackDispatcher() { Workmanager().executeTask((task, inputData) async { switch (task) { case fetchBackground: try { Position position = await Geolocator.getCurrentPosition(); print( 'Latitude: ${position.latitude}, Longitude: ${position.longitude}'); } catch (error) { print('Error getting position: $error'); } break; } return Future.value(true); }); } void main() { WidgetsFlutterBinding.ensureInitialized(); Workmanager().initialize(callbackDispatcher); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'MyApp Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { Future<Position> _determinePosition() async { bool serviceEnabled; LocationPermission permission; serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { return Future.error('Location services are disabled.'); } permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { return Future.error('Location permissions are denied'); } } if (permission == LocationPermission.deniedForever) { return Future.error( 'Location permissions are permanently denied, we cannot request permissions.'); } return await Geolocator.getCurrentPosition(); } void _getPosition() async { try { Position position = await _determinePosition(); print('Latitude: ${position.latitude}, Longitude: ${position.longitude}'); } catch (error) { print('Error getting position: $error'); } } @override void initState() { super.initState(); _getPosition(); Workmanager().initialize( callbackDispatcher, isInDebugMode: true, ); Workmanager().registerPeriodicTask( "backgroundLocationTask", fetchBackground, frequency: Duration(seconds: 10), //frequency: Duration(minutes: 15), // Set every 15 minutes ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), ); } }
后台报错信息
D/FlutterGeolocator(19802): Disposing Geolocator services E/FlutterGeolocator(19802): Geolocator position updates stopped D/FlutterGeolocator(19802): Stopping location service. I/WM-WorkerWrapper(19802): Work [ id=fc438a55-d63a-47e7-836e-5e0bfac59d2d, tags={ be.tramckrijte.workmanager.BackgroundWorker } ] was cancelled I/WM-WorkerWrapper(19802): java.util.concurrent.CancellationException: Task was cancelled. I/WM-WorkerWrapper(19802): at androidx.work.impl.utils.futures.AbstractFuture.cancellationExceptionWithCause(AbstractFuture.java:1184) I/WM-WorkerWrapper(19802): at androidx.work.impl.utils.futures.AbstractFuture.getDoneValue(AbstractFuture.java:514) I/WM-WorkerWrapper(19802): at androidx.work.impl.utils.futures.AbstractFuture.get(AbstractFuture.java:475) I/WM-WorkerWrapper(19802): at androidx.work.impl.WorkerWrapper$2.run(WorkerWrapper.java:311) I/WM-WorkerWrapper(19802): at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91) I/WM-WorkerWrapper(19802): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) I/WM-WorkerWrapper(19802): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) I/WM-WorkerWrapper(19802): at java.lang.Thread.run(Thread.java:919) D/FlutterGeolocator(19802): Unbinding from location service. D/FlutterGeolocator(19802): Destroying location service. D/FlutterGeolocator(19802): Stopping location service. D/FlutterGeolocator(19802): Destroyed location service.
解决方法
1. 补全后台定位权限
- Android端:在
android/app/src/main/AndroidManifest.xml中添加后台定位权限:<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> - iOS端:在
ios/Runner/Info.plist中添加后台定位描述:<key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <string>需要后台定位权限以获取您的位置信息</string> <key>NSLocationAlwaysUsageDescription</key> <string>需要后台定位权限以获取您的位置信息</string> - 代码中请求后台权限:修改
_determinePosition方法,明确请求永久定位权限:permission = await Geolocator.requestPermission(locationPermissionLevel: LocationPermissionLevel.locationAlways);
2. 修正Workmanager任务配置
- 移除
initState中重复的Workmanager().initialize调用(main方法已初始化)。 - 配置任务约束条件,避免系统轻易取消任务,同时遵守Android周期任务最小间隔要求(15分钟):
Workmanager().registerPeriodicTask( "backgroundLocationTask", fetchBackground, frequency: const Duration(minutes: 15), constraints: const Constraints( networkType: NetworkType.not_required, requiresBatteryNotLow: false, requiresCharging: false, requiresDeviceIdle: false, requiresStorageNotLow: false, ), backoffPolicy: BackoffPolicy.exponential, backoffPolicyDelay: const Duration(seconds: 10), ); - 调试时可改用
registerOneOffTask配合initialDelay测试后台执行:Workmanager().registerOneOffTask( "testBackgroundLocation", fetchBackground, initialDelay: const Duration(seconds: 10), );
3. 适配Geolocator后台定位参数
在后台任务的定位请求中指定适配后台的LocationSettings,提升定位成功率:
@pragma('vm:entry-point') void callbackDispatcher() { Workmanager().executeTask((task, inputData) async { switch (task) { case fetchBackground: try { LocationSettings locationSettings = const LocationSettings( accuracy: LocationAccuracy.balanced, distanceFilter: 10, ); Position position = await Geolocator.getCurrentPosition(locationSettings: locationSettings); print('Latitude: ${position.latitude}, Longitude: ${position.longitude}'); } catch (error) { print('Error getting position: $error'); } break; } return Future.value(true); }); }
4. 适配系统后台限制
- Android 12+对后台任务执行时间有严格限制,确保定位逻辑尽量简洁,减少耗时。
- 部分厂商(如小米、华为)有额外的后台管控,需在系统设置中为应用开启"后台运行权限"或"自启动权限"。
内容的提问来源于stack exchange,提问作者user572575
相关产品推荐
相关产品推荐

