You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在原生端每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}'.");
    }
  }

问题分析与修复方案

核心问题点

  1. Flutter端仅单次获取位置:sendTasksToNative()里用getCurrentPosition()只会获取一次位置,没有实现每40秒定时获取的逻辑。
  2. 后台位置更新未配置:Android端缺少后台位置更新的必要配置(如前台服务、权限),同时Flutter的Geolocator也未设置后台定位模式。
  3. LocationSettings参数冲突:设置了distanceFilter:100,意味着只有位置移动超过100米才会更新,和“每40秒获取一次”的需求矛盾。
  4. 原生端未绑定定时触发逻辑: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 07:19:50