Kotlin 1.9.20 后,CLLocationDelegate 在 KMM 项目中无法正常工作

问题描述 投票:0回答:1
我有一个 KMM 项目,需要访问用户的位置。我的 locationClient 是用 Kotlin 编写的。

一切都按预期工作,但在将 Kotlin 从 1.8.20 更新到 1.9.20 后,我看到了奇怪的行为。

我按预期获得了前几个位置,但随后代表停止发回位置。我通常会在停止之前获得 5 或 6 个位置,但不会超过 10 个。我在模拟器和真实设备上都对此进行了测试,并得到相同的结果。

在 Swift 端,开始发送位置的触发器位于“onAppear”块中,离开和返回会触发另外几个位置经过,但随后它们会再次停止。

当位置停止时,手机仍然认为应用程序正在侦听位置,并继续显示相应的隐私通知。当我离开并停止收听位置时,此信息已正确删除。

位置客户:

actual class LocationProviderImpl : LocationProvider { private val locationManager = CLLocationManager() private class LocationDelegate : NSObject(), CLLocationManagerDelegateProtocol { var onLocationUpdate: ((Location?) -> Unit)? = null override fun locationManager(manager: CLLocationManager, didUpdateLocations: List<*>) { //This is correctly called for the first few locations then stops didUpdateLocations.firstOrNull()?.let { val location = it as CLLocation location.coordinate.useContents { onLocationUpdate?.invoke(Location(latitude, longitude, it.altitude)) } } } override fun locationManager(manager: CLLocationManager, didFailWithError: NSError) { //This never gets called onLocationUpdate?.invoke(null) } } override fun getCurrentLocationAsFlow(): Flow<Location?> = callbackFlow { locationManager.requestAlwaysAuthorization() locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.distanceFilter = kCLDistanceFilterNone val locationDelegate = LocationDelegate() locationDelegate.onLocationUpdate = { location -> trySend( location ).isSuccess } locationManager.delegate = locationDelegate locationManager.startUpdatingLocation() awaitClose { //This is never called locationManager.stopUpdatingLocation() } }.flowOn(Dispatchers.IO) fun requestBackground() { locationManager.allowsBackgroundLocationUpdates = true } fun stopBackground() { locationManager.allowsBackgroundLocationUpdates = false } fun stopLocationUpdates() { locationManager.stopUpdatingLocation() } }
其用法如下:

object GpsService { private lateinit var locationClient: LocationProviderImpl private var initialized = false private var serviceScope = CoroutineScope(Dispatchers.IO) fun initialize(appModule: AppModule) { if (!initialized) { locationClient = LocationProviderImpl() } } fun startLocationListener() { locationClient.getCurrentLocationAsFlow().cancellable() .catch { e -> e.printStackTrace() } .mapNotNull { it } .onEach { location -> // send location to view _state.update { it.copy( currentLocation = location, formattedAltitude = getFormattedAltitude(location.altitude) ) } }.launchIn(serviceScope) }
我没有收到任何错误或警告(并且在各处设置了断点/ println()),也没有遇到任何错误或 catch 块,我只是在前几个位置之后停止返回任何位置。在 Kotlin 1.8.20 上一切正常!

kotlin core-location kotlin-multiplatform
1个回答
0
投票
Kotlin 有自己的内存模型,但是当您甚至从 Kotlin 代码创建 ObjC 对象时,它们也会遵循 ObjC 内存模型。

在 iOS 世界中,名为

delegate

 的字段通常(总是在系统框架中)存储对对象的弱引用 - 这意味着该对象不负责该对象的生命周期,因此当该对象被销毁时,该属性为自动设置为空。这样做是为了防止循环引用。请参阅
此问题了解更多详细信息。如果您计划从 Kotlin 实现更多 iOS 功能,请查看一些有关自动引用计数 (ARC) 的文章。

在您的代码中,一旦

locationDelegate

 暂停函数执行,
awaitClose
 就会被释放。

所以你需要将它存储在某个地方。例如,在类变量中。

使用您当前的代码,这可能会很棘手,因为

getCurrentLocationAsFlow

 可以从不同的地方多次调用,因此旧的委托将被新的调用覆盖 - 您可能会考虑在 
init
 期间创建一个委托并将其存储为
let
,并向该对象添加 
onLocationUpdate
 处理程序。

© www.soinside.com 2019 - 2024. All rights reserved.