Delphi 11.3开发Android/iOS后台每5分钟获取GPS坐标问题求助
Delphi 11.3 跨平台后台定时获取GPS坐标方案
问题现状
使用Delphi 11.3开发应用,需实现Android后台每5分钟获取一次GPS坐标,同时兼容iOS。当前代码调用FLocationManager.requestLocationUpdates后应用无提示崩溃,注释该代码段则正常运行,但无法持续获取位置数据。
Android端解决方案
崩溃原因分析
- 主线程阻塞风险:直接使用
TJLooper.JavaClass.getMainLooper将定位回调绑定到UI线程,可能触发系统ANR(应用无响应)或崩溃 - Android后台限制:Android 8.0+对后台应用的定位行为有严格限制,无前台服务支撑时,系统会快速回收定位资源
- 权限缺失:Android 10+需要
ACCESS_BACKGROUND_LOCATION权限才能在后台获取位置,当前代码未申请该权限
修复步骤
补充后台定位权限
在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) ], ...);基于前台服务托管定位逻辑
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;修正LocationListener回调
确保回调逻辑安全传递到服务:procedure TLocationListener.onLocationChanged(location: JLocation); begin if Assigned(FParent) then (FParent as TLocationForegroundService).HandleLocationUpdate(location); end;启动前台服务
在权限通过后启动服务: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端解决方案
配置与实现
Info.plist后台权限配置
添加定位相关权限描述和后台模式:<key>NSSLocationAlwaysAndWhenInUseUsageDescription</key> <string>需要后台持续获取位置信息</string> <key>NSSLocationWhenInUseUsageDescription</key> <string>需要获取位置信息</string> <key>UIBackgroundModes</key> <array> <string>location</string> </array>使用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
相关产品推荐
相关产品推荐

