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

Delphi 11.3开发Android/iOS后台每5分钟获取GPS坐标问题求助

Delphi 11.3 跨平台后台定时获取GPS坐标方案

问题现状

使用Delphi 11.3开发应用,需实现Android后台每5分钟获取一次GPS坐标,同时兼容iOS。当前代码调用FLocationManager.requestLocationUpdates后应用无提示崩溃,注释该代码段则正常运行,但无法持续获取位置数据。


Android端解决方案

崩溃原因分析

  1. 主线程阻塞风险:直接使用TJLooper.JavaClass.getMainLooper将定位回调绑定到UI线程,可能触发系统ANR(应用无响应)或崩溃
  2. Android后台限制:Android 8.0+对后台应用的定位行为有严格限制,无前台服务支撑时,系统会快速回收定位资源
  3. 权限缺失:Android 10+需要ACCESS_BACKGROUND_LOCATION权限才能在后台获取位置,当前代码未申请该权限

修复步骤

  1. 补充后台定位权限
    在AndroidManifest.template.xml中添加权限声明:

    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    

    同时在权限请求列表中加入该权限:

    PermissionsService.RequestPermissions([
      JStringToString(TJManifest_permission.JavaClass.READ_PHONE_STATE),
      JStringToString(TJManifest_permission.JavaClass.ACCESS_COARSE_LOCATION),
      JStringToString(TJManifest_permission.JavaClass.ACCESS_FINE_LOCATION),
      JStringToString(TJManifest_permission.JavaClass.ACCESS_BACKGROUND_LOCATION),
      JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE),
      JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE),
      JStringToString(TJManifest_permission.JavaClass.CAMERA)
    ], ...);
    
  2. 基于前台服务托管定位逻辑
    Android后台持续定位必须依赖前台服务,避免被系统回收。创建前台服务类:

    type
      TLocationForegroundService = class(TAndroidService)
      private
        FLocationManager: JLocationManager;
        FLocationListener: TLocationListener;
        procedure StartForegroundNotification;
        procedure HandleLocationUpdate(location: JLocation);
      public
        function StartCommand(const intent: JIntent; flags, startId: Integer): Integer; override;
        procedure Create; override;
        procedure Destroy; override;
      end;
    
    implementation
    
    procedure TLocationForegroundService.Create;
    var
      LocationManagerService: JObject;
    begin
      inherited;
      // 初始化LocationManager
      LocationManagerService := TAndroidHelper.Context.getSystemService(TJContext.JavaClass.LOCATION_SERVICE);
      FLocationManager := TJLocationManager.Wrap((LocationManagerService as ILocalObject).GetObjectID);
      FLocationListener := TLocationListener.Create(Self);
      // 启动前台通知
      StartForegroundNotification;
    end;
    
    procedure TLocationForegroundService.StartForegroundNotification;
    var
      NotificationBuilder: JNotificationCompat_Builder;
      NotificationChannel: JNotificationChannel;
      NotificationManager: JNotificationManager;
      Intent: JIntent;
      PendingIntent: JPendingIntent;
    begin
      // 创建通知渠道(Android 8.0+必需)
      NotificationManager := TJNotificationManager.Wrap(TAndroidHelper.Context.getSystemService(TJContext.JavaClass.NOTIFICATION_SERVICE));
      NotificationChannel := TJNotificationChannel.JavaClass.init('location_channel', StrToJCharSequence('定位服务'), TJNotificationManager.JavaClass.IMPORTANCE_LOW);
      NotificationManager.createNotificationChannel(NotificationChannel);
    
      // 构建返回应用的PendingIntent
      Intent := TJIntent.JavaClass.init(TAndroidHelper.Context, TAndroidHelper.Activity.getClass);
      PendingIntent := TJPendingIntent.JavaClass.getActivity(TAndroidHelper.Context, 0, Intent, 0);
    
      // 构建前台通知
      NotificationBuilder := TJNotificationCompat_Builder.JavaClass.init(TAndroidHelper.Context, 'location_channel');
      NotificationBuilder.setContentTitle(StrToJCharSequence('后台定位中'));
      NotificationBuilder.setContentText(StrToJCharSequence('应用正在后台获取位置'));
      NotificationBuilder.setSmallIcon(TJResource.JavaClass.drawable.ic_stat_notify);
      NotificationBuilder.setContentIntent(PendingIntent);
    
      // 启动前台服务
      StartForeground(1, NotificationBuilder.build);
    end;
    
    function TLocationForegroundService.StartCommand(const intent: JIntent; flags, startId: Integer): Integer;
    begin
      // 设置5分钟(300000ms)间隔,距离变化0米触发更新
      FLocationManager.requestLocationUpdates(TJLocationManager.JavaClass.GPS_PROVIDER, 300000, 0, FLocationListener, nil);
      Result := TJService.JavaClass.START_STICKY;
    end;
    
    procedure TLocationForegroundService.HandleLocationUpdate(location: JLocation);
    var
      lc: TLocationCoord2D;
    begin
      lc := TLocationCoord2D.Create(location.getLatitude, location.getLongitude);
      // 线程同步更新UI(避免跨线程操作)
      TThread.Synchronize(nil, procedure
      begin
        if Assigned(Form1) then
          Form1.Memo1.Lines.Add(lc.Latitude.ToString + ' - ' + lc.Longitude.ToString);
      end);
    end;
    
    procedure TLocationForegroundService.Destroy;
    begin
      // 停止位置更新,释放资源
      if Assigned(FLocationManager) and Assigned(FLocationListener) then
        FLocationManager.removeUpdates(FLocationListener);
      inherited;
    end;
    
  3. 修正LocationListener回调
    确保回调逻辑安全传递到服务:

    procedure TLocationListener.onLocationChanged(location: JLocation);
    begin
      if Assigned(FParent) then
        (FParent as TLocationForegroundService).HandleLocationUpdate(location);
    end;
    
  4. 启动前台服务
    在权限通过后启动服务:

    procedure TForm1.FormShow(Sender: TObject);
    begin
      PermissionsService.RequestPermissions([
        // 包含ACCESS_BACKGROUND_LOCATION的所有权限
      ], procedure(const APermissions: TClassicStringDynArray; const AGrantResults: TClassicPermissionStatusDynArray)
      begin
        if AllPermissionsGranted(AGrantResults) then
        begin
          // 启动前台定位服务
          TAndroidHelper.Context.startService(TJIntent.JavaClass.init(TAndroidHelper.Context, TLocationForegroundService.Class));
        end
        else
          TDialogService.ShowMessage('需授予所有权限才能使用定位服务');
      end);
    end;
    
    function TForm1.AllPermissionsGranted(const AGrantResults: TClassicPermissionStatusDynArray): Boolean;
    var
      Status: TPermissionStatus;
    begin
      Result := True;
      for Status in AGrantResults do
        if Status <> TPermissionStatus.Granted then
        begin
          Result := False;
          Break;
        end;
    end;
    

iOS端解决方案

配置与实现

  1. Info.plist后台权限配置
    添加定位相关权限描述和后台模式:

    <key>NSSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>需要后台持续获取位置信息</string>
    <key>NSSLocationWhenInUseUsageDescription</key>
    <string>需要获取位置信息</string>
    <key>UIBackgroundModes</key>
    <array>
      <string>location</string>
    </array>
    
  2. 使用FMX TLocationSensor组件
    直接用跨平台组件实现定时定位:

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      // 设置5分钟更新间隔
      LocationSensor1.UpdateInterval := 300000;
    end;
    
    procedure TForm1.RequestLocationPermission;
    begin
      PermissionsService.RequestPermissions([LOCATION_ALWAYS_PERMISSION],
      procedure(const APermissions: TClassicStringDynArray; const AGrantResults: TClassicPermissionStatusDynArray)
      begin
        if AGrantResults[0] = TPermissionStatus.Granted then
          LocationSensor1.Active := True
        else
          TDialogService.ShowMessage('需授予始终允许的定位权限才能后台获取位置');
      end);
    end;
    
    procedure TForm1.LocationSensor1LocationChanged(Sender: TObject; const OldLocation, NewLocation: TLocationCoord2D);
    begin
      Memo1.Lines.Add(NewLocation.Latitude.ToString + ' - ' + NewLocation.Longitude.ToString);
    end;
    

内容的提问来源于stack exchange,提问作者lorife

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 12:50:45