如何在原生端每40秒获取高精度定位并实现后台更新?
问题:原生端每40秒获取精准位置+后台更新功能异常
我想要在原生Android端实现每40秒获取精准当前位置,同时支持后台位置更新,但功能无法正常运行。我通过Flutter的Platform Channel传递数据,同时使用了Geolocator插件,但效果未达预期。
原生Android代码(Java)
private void processLocation(Location location) { double currentLat = location.getLatitude(); double currentLng = location.getLongitude(); double accuracy = location.getAccuracy(); Log.i(TAG, "Current Location: Lat: " + currentLat + ", Lng: " + currentLng); long currentTimeMillis = System.currentTimeMillis(); Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(currentTimeMillis); int currentHour = calendar.get(Calendar.HOUR_OF_DAY); int currentMinute = calendar.get(Calendar.MINUTE); // Process each employee/task for (HashMap<String, Object> task : empData) { double officeLat = (double) task.get("office_lat"); double officeLng = (double) task.get("office_long"); double cabinLat = (double) task.get("cabin_lat"); double cabinLng = (double) task.get("cabin_long"); int id = (int) task.get("id"); // Calculate distance to office float[] results = new float[1]; Location.distanceBetween(currentLat, currentLng, officeLat, officeLng, results); float distanceInMeters = results[0]; // Calculate distance to cabin Location.distanceBetween(currentLat, currentLng, cabinLat, cabinLng, results); float cabinDistance = results[0]; // Retrieve timing parameters from task int officeCheckInTime = (int) task.get("office_checkin_time"); int graceTime = (int) task.get("grace_time"); int checkOutTime = (int) task.get("office_checkout_time"); int breakOutTime = (int) task.get("break_out_time"); // --- Office Check-In/Check-Out Logic --- if (distanceInMeters <= 50) { // User is within office radius // Office check-in // if (!checkInStatus.getOrDefault(id, false)) { int totalMinutesLate = (currentHour * 60 + currentMinute) - (officeCheckInTime * 60); if (totalMinutesLate <= graceTime) { sendNotification("Check-in successful", "Check-in Successful. Distance: " + distanceInMeters + " meters.", id + 7643, this); checkInStatus.put(id, true); } else { String lateTime = formatTime(totalMinutesLate - graceTime); sendNotification("Check-in successful", "Check-in Late by " + lateTime + ". Distance: " + distanceInMeters + " meters.", id + 7643, this); sendNotification("Accuracy", "Accuracy is" + accuracy, id + 435, this); checkInStatus.put(id, true); } // } // --- Cabin Break Logic --- if (checkInStatus.getOrDefault(id, false)) { if (cabinDistance > 10) { // User is outside cabin radius if (!lastBreakTime.containsKey(id)) { // Start break timer lastBreakTime.put(id, System.currentTimeMillis()); sendNotification("Break Started", "Your Break has Started. Cabin Distance: " + cabinDistance + " meters.", id + 1122, this); Log.i(TAG, "User left cabin, starting break timer."); } else { // Check if break duration exceeds the allowed break-out time long breakDuration = (System.currentTimeMillis() - lastBreakTime.get(id)) / 60000; // Convert ms to minutes if (breakDuration >= breakOutTime) { if (!checkOutStatus.getOrDefault(id, false)) { sendNotification("Early Checkout", "You left your cabin for " + breakOutTime + " minutes. Auto check-out.", id + 1122, this); checkOutStatus.put(id, true); Log.i(TAG, "User auto checked out due to long absence from cabin."); } } } } else { // User is within cabin radius if (lastBreakTime.containsKey(id)) { long breakDuration = (System.currentTimeMillis() - lastBreakTime.get(id)) / 60000; if (breakDuration < breakOutTime) { lastBreakTime.remove(id); sendNotification("Break Ended", "You returned to your cabin. Break timer reset.", id + 1122, this); Log.i(TAG, "User returned to cabin, break timer reset."); } } } } // Office check-out (if within office radius) if (!checkOutStatus.getOrDefault(id, false)) { int totalMinutesLateCheckout = (currentHour * 60 + currentMinute) - (checkOutTime * 60); if (totalMinutesLateCheckout == 0) { sendNotification("Check-out successful", "Check-out Successful.", id + 3324, this); checkOutStatus.put(id, true); } } else { Log.i(TAG, "User has already checked out."); } } else { // --- Outside Office Radius Check-Out --- if (!checkOutStatus.getOrDefault(id, false)) { int totalMinutesEarly = (checkOutTime * 60) - (currentHour * 60 + currentMinute); if (totalMinutesEarly > 0) { String earlyTime = formatTime(totalMinutesEarly); sendNotification("Check-out successful", "You left " + earlyTime + " before time. Distance: " + distanceInMeters + " meters.", id + 3243, this); checkOutStatus.put(id, true); } } else { Log.i(TAG, "User has already checked out."); } } } }
Flutter端Platform Channel代码(Dart)
Future<void> sendTasksToNative() async { try { const LocationSettings locationSettings = LocationSettings( accuracy: LocationAccuracy.high, distanceFilter: 100, ); Position position = await Geolocator.getCurrentPosition( locationSettings: locationSettings); var accuracy = await Geolocator.getLocationAccuracy(); print( "Current Latitude:::::::::::::: ${position.latitude.toString()} Current Longitude:::::::::::::: ${position.longitude.toString()} :::::::::::::: Accueracy : $accuracy"); var empList = [ { "id": 1, "company_name": "Softlinks", "emp_name": "Abdullah Shamoon", "break_time": 13, "break_out_time": 10, "office_checkin_time": 10, "office_checkout_time": 19, "grace_time": 10, "office_lat": position.latitude, "office_long": position.longitude, "cabin_lat": position.latitude, "cabin_long": position.longitude } ]; List<Map<String, dynamic>> empData = empList.toList().cast<Map<String, dynamic>>(); await platform.invokeMethod('sendData', { 'emp_data': empData, }); print( "All Employee Data Sent From flutter to Native: ${empData.toString()}"); } on PlatformException catch (e) { print("Failed to send tasks to native: '${e.message}'."); } }
问题分析与修复方案
核心问题点
- Flutter端仅单次获取位置:
sendTasksToNative()里用getCurrentPosition()只会获取一次位置,没有实现每40秒定时获取的逻辑。 - 后台位置更新未配置:Android端缺少后台位置更新的必要配置(如前台服务、权限),同时Flutter的Geolocator也未设置后台定位模式。
- LocationSettings参数冲突:设置了
distanceFilter:100,意味着只有位置移动超过100米才会更新,和“每40秒获取一次”的需求矛盾。 - 原生端未绑定定时触发逻辑:
processLocation()方法没有被定时调用的机制,也没有监听系统位置更新的代码。
修复步骤
1. Flutter端实现定时获取位置
把单次获取改为定时循环,使用Timer.periodic每40秒触发一次:
Timer? _locationTimer; void startLocationUpdates() { _locationTimer?.cancel(); // 每40秒触发一次位置获取与数据发送 _locationTimer = Timer.periodic(const Duration(seconds: 40), (timer) { sendTasksToNative(); }); } // 页面销毁时取消定时器 @override void dispose() { _locationTimer?.cancel(); super.dispose(); }
同时修改LocationSettings,移除距离过滤,确保即使不动也能定时获取:
const LocationSettings locationSettings = LocationSettings( accuracy: LocationAccuracy.high, distanceFilter: 0, // 取消距离过滤,保证定时更新 );
2. Android端配置后台定位权限与服务
- 在
AndroidManifest.xml添加权限:
<!-- 前台服务权限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" /> <!-- 后台定位权限(Android 10+) --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
- 实现前台服务用于后台位置更新:
创建LocationForegroundService类,在服务中通过FusedLocationProviderClient设置定时位置请求:
public class LocationForegroundService extends Service { private static final int NOTIFICATION_ID = 12345; private FusedLocationProviderClient fusedLocationClient; private LocationCallback locationCallback; @Override public void onCreate() { super.onCreate(); fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); locationCallback = new LocationCallback() { @Override public void onLocationResult(LocationResult locationResult) { if (locationResult != null && locationResult.getLastLocation() != null) { // 调用位置处理逻辑 ((YourMainActivity) getApplicationContext()).processLocation(locationResult.getLastLocation()); } } }; } @Override public int onStartCommand(Intent intent, int flags, int startId) { startForeground(NOTIFICATION_ID, buildNotification()); startLocationUpdates(); return START_STICKY; } private void startLocationUpdates() { LocationRequest locationRequest = LocationRequest.create(); locationRequest.setInterval(40000); // 40秒间隔 locationRequest.setFastestInterval(40000); locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); try { fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper()); } catch (SecurityException e) { e.printStackTrace(); } } private Notification buildNotification() { NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "LOCATION_CHANNEL") .setContentTitle("位置更新中") .setContentText("正在后台获取位置") .setSmallIcon(R.drawable.ic_location) .setPriority(NotificationCompat.PRIORITY_LOW); // Android 8.0+ 创建通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel("LOCATION_CHANNEL", "位置服务", NotificationManager.IMPORTANCE_LOW); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } return builder.build(); } @Override public void onDestroy() { fusedLocationClient.removeLocationUpdates(locationCallback); super.onDestroy(); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } }
在原生Activity或Flutter端通过Platform Channel启动服务:
Intent serviceIntent = new Intent(this, LocationForegroundService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(serviceIntent); } else { startService(serviceIntent); }
3. 修正原生端逻辑问题
- 恢复
checkInStatus判断,避免每次位置更新重复发送签到通知:
// 恢复判断,防止重复签到 if (!checkInStatus.getOrDefault(id, false)) { // 原有的签到逻辑代码 }
- 修正签出逻辑的严格判断,允许1分钟误差:
if (totalMinutesLateCheckout >= 0 && totalMinutesLateCheckout <= 1) { sendNotification("Check-out successful", "Check-out Successful.", id + 3324, this); checkOutStatus.put(id, true); }
4. Flutter端申请完整权限
确保申请到定位权限(包括后台权限):
Future<void> requestPermissions() async { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { // 提示用户开启位置服务 return; } LocationPermission permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { // 权限被拒绝,提示用户 return; } } if (permission == LocationPermission.deniedForever) { // 引导用户前往设置页面开启权限 return; } // Android 10+ 请求后台定位权限 if (Platform.isAndroid) { await Geolocator.requestPermission(); } }
内容的提问来源于stack exchange,提问作者ABDULLAH SHAMOON
相关产品推荐
相关产品推荐

