Android12 onLocationChanged返回无效JLocation致服务崩溃
问题根因
崩溃日志已经明确给出核心错误:JNI检测到代码尝试在java.util.ArrayList实例上调用android.location.Location.getLatitude()方法,本质是回调声明的参数类型和Android 12实际传入的参数类型不匹配。
这是Delphi Android SDK桥接层在Android 12+上的兼容性bug:从Android 12开始,系统定位服务的onLocationChanged回调在部分场景(包括前台服务绑定、高频定位返回)下,传入的第一个参数不再是单个Location对象,而是包含多个Location元素的ArrayList集合;但Delphi RTL中的LocationListener接口声明没有适配该变更,默认将入参强转为JLocation类型直接调用方法,就会触发JNI类型校验导致进程崩溃。
Android 11及更早版本系统只会在回调中传递单个Location对象,因此旧版本系统上相同逻辑可正常运行,该问题和定位权限、前台服务配置无关。
修复方案
不要直接使用Delphi默认生成的onLocationChanged回调签名,手动修正参数解析逻辑:
- 首先将回调的入参类型改为通用
JObject,先判断传入对象的实际运行时类型 - 如果传入对象是
ArrayList类型,取集合的最后一个元素(即最新的定位结果)转换为JLocation后使用 - 如果传入对象本身就是
JLocation类型,直接转换后使用即可,该逻辑可兼容所有Android版本
修复后的可运行代码示例:
procedure TLocationListener.onLocationChanged(locationObj: JObject); var Accuracy: double; location: JLocation; locationList: JArrayList; begin if locationObj = nil then Exit; if locationObj is JArrayList then begin // Android 12+ 批量返回场景,取最后一个为最新定位点 locationList := TJArrayList.Wrap((locationObj as ILocalObject).GetObjectID); if locationList.size = 0 then Exit; location := TJLocation.Wrap((locationList.get(locationList.size - 1) as ILocalObject).GetObjectID); end else if locationObj is JLocation then begin // Android 11及以下版本 单对象返回场景 location := TJLocation.Wrap((locationObj as ILocalObject).GetObjectID); end else Exit; // 后续逻辑和原有代码完全一致 if location.hasAccuracy then begin LocalLog('onLocationChanged: hasAccuracy'); Accuracy := location.getAccuracy; LocalLog('onLocationChanged: Accuracy='+Accuracy.ToString); end else LocalLog('onLocationChanged: NOT hasAccuracy'); end;
补充说明
- 该参数类型变更是Android 12对定位API的官方行为调整:新增批量定位结果返回能力以降低系统功耗,当应用申请高精度定位、定位回调触发频率达到阈值时,系统会自动将多个定位点打包为
ArrayList通过同一个onLocationChanged回调返回 - 若不需要批量返回的历史定位点,直接取
ArrayList的最后一个元素,就是和旧版本逻辑完全一致的最新定位结果 - Delphi 11.3之前的自带RTL没有适配该系统行为变更,不管是直接实现
JLocationListener接口还是使用内置的TLocationSensor组件都会遇到该崩溃,要么升级Delphi到已适配的版本,要么手动按上述逻辑修正参数解析。
内容的提问来源于stack exchange,提问作者Bart Kindt
相关产品推荐
相关产品推荐

