一切都按预期工作,但在将 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 上一切正常!
在 iOS 世界中,名为
delegate
的字段通常(总是在系统框架中)存储对对象的弱引用 - 这意味着该对象不负责该对象的生命周期,因此当该对象被销毁时,该属性为自动设置为空。这样做是为了防止循环引用。请参阅此问题了解更多详细信息。如果您计划从 Kotlin 实现更多 iOS 功能,请查看一些有关自动引用计数 (ARC) 的文章。 在您的代码中,一旦
locationDelegate
暂停函数执行,
awaitClose
就会被释放。所以你需要将它存储在某个地方。例如,在类变量中。
使用您当前的代码,这可能会很棘手,因为
getCurrentLocationAsFlow
可以从不同的地方多次调用,因此旧的委托将被新的调用覆盖 - 您可能会考虑在
init
期间创建一个委托并将其存储为
let
,并向该对象添加
onLocationUpdate
处理程序。