我有一个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块被命中,我只是在最初的几个之后停止获取任何位置。这一切都在Kotlin1.8.20中运行得很好!
1条答案
按热度按时间gzjq41n41#
Kotlin有它自己的内存模型,但是当你从Kotlin代码创建ObjC对象时,它们遵循ObjC内存模型。
在iOS世界中,名为
delegate
的字段通常(总是在系统框架中)存储一个对象的弱引用-这意味着这个对象不负责对象的生命周期,所以当对象被销毁时,该属性会自动设置为null。这样做是为了防止保留周期。有关详细信息,请参阅this question。如果您计划从Kotlin,查看一些关于自动引用计数(ARC)的文章。在您的代码中,
locationDelegate
将在awaitClose
暂停函数执行时立即释放。所以你需要把它存储在某个地方。比如,在一个类变量中。
对于您当前的代码,这可能是棘手的,因为
getCurrentLocationAsFlow
可以从不同的地方被多次调用,因此旧的委托将被新的调用覆盖-您可以考虑在init
期间创建一个委托并存储为let
,并为该对象添加onLocationUpdate
处理程序。