Flutter应用Android 12后台定位失效,Android 9正常求助
Android 12下Flutter后台定位失效的解决方案
核心原因
Android 12(API 31+)对后台操作施加了更严格的限制:
- 后台AlarmManager的触发频率被系统限制,无法像低版本那样高频执行任务
- 后台定位需要额外的权限声明和规范的请求流程
- 系统会限制普通后台任务的资源访问权限,Alarm触发的任务可能无法调用定位服务
具体修复步骤
1. 完善权限配置
确保AndroidManifest.xml中添加符合Android 12要求的权限:
<!-- 前台定位权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 后台定位权限 --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <!-- Android 12+ 后台任务必需通知权限 --> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- 声明依赖GPS硬件 --> <uses-feature android:name="android.hardware.location.gps" />
同时用permission_handler包主动请求权限,确保用户授予始终允许定位权限和通知权限:
import 'package:permission_handler/permission_handler.dart'; Future<void> requestRequiredPermissions() async { // 先请求前台定位权限 var locationStatus = await Permission.locationWhenInUse.request(); if (locationStatus.isGranted) { // 再请求后台定位权限 await Permission.locationAlways.request(); } // 请求通知权限(Android 12+ 后台任务必需) await Permission.notification.request(); }
2. 替换AndroidAlarmManager为WorkManager
Android 12限制了后台Alarm的执行,workmanager包更适配系统省电策略,是后台周期性任务的标准解决方案:
依赖配置
在pubspec.yaml中添加:
dependencies: workmanager: ^0.5.1
初始化与任务注册
import 'package:workmanager/workmanager.dart'; // 后台任务回调 void locationTaskDispatcher() { Workmanager().executeTask((task, inputData) async { await liveLocation(); // 调用你的定位逻辑 return Future.value(true); }); } void main() async { WidgetsFlutterBinding.ensureInitialized(); // 初始化WorkManager await Workmanager().initialize( locationTaskDispatcher, isInDebugMode: false, ); // 注册周期性任务(Android强制要求间隔≥15分钟) await Workmanager().registerPeriodicTask( "location-tracking", "track-location", frequency: const Duration(minutes: 15), initialDelay: const Duration(seconds: 5), constraints: Constraints( networkType: NetworkType.connected, // 可选:仅在联网时执行 ), ); runApp(const MyApp()); }
3. 优化定位获取逻辑
修改liveLocation函数,添加错误处理和后台适配参数:
static Future<void> liveLocation() async { try { await Firebase.initializeApp(); final prefs = await SharedPreferences.getInstance(); await prefs.reload(); double? storedLongitude = prefs.getDouble('trackingLongitude'); double? storedLatitude = prefs.getDouble('trackingLatitude'); String? empname = prefs.getString('empname'); // 配置后台可用的定位参数 final trackingLocation = await Geolocator.getCurrentPosition( locationSettings: const LocationSettings( accuracy: LocationAccuracy.high, distanceFilter: 10, // 移动10米再更新,节省电量 ), timeout: const Duration(seconds: 10), // 设置超时避免阻塞 ); // 距离计算逻辑(保留原代码) const R = 6371; final dLat = (trackingLocation.latitude - storedLatitude!) * pi / 180; final dLon = (trackingLocation.longitude - storedLongitude!) * pi / 180; final a = sin(dLat / 2) * sin(dLat / 2) + cos(trackingLocation.latitude * pi / 180) * cos(storedLatitude * pi / 180) * sin(dLon / 2) * sin(dLon / 2); final c = 2 * atan2(sqrt(a), sqrt(1 - a)); final d = R * c * 1000; // 更新本地存储与Firebase await prefs.setDouble('trackingLongitude', trackingLocation.longitude); await prefs.setDouble('trackingLatitude', trackingLocation.latitude); if (empname != null) { await FirebaseFirestore.instance .collection(tasksCollectionName) .doc(empname) .collection("location") .doc("location") .set({ 'latitude': trackingLocation.latitude, 'longitude': trackingLocation.longitude, 'arriveTime': DateTime.now().toString().substring(11, 16), }); } } catch (e) { print('定位或数据上传失败: $e'); } }
4. 关键注意事项
- Android 12强制要求周期性后台任务间隔≥15分钟,设置更短间隔会被系统忽略
- 后台定位时系统可能降低精度,若需高精度,建议前台启动定位服务后后台延续使用
- 测试时需将应用切换到后台,等待WorkManager触发任务,调试模式下的即时执行不代表真实后台行为
内容的提问来源于stack exchange,提问作者Milad alsabbagh
相关产品推荐
相关产品推荐

