Shi*_*men 14 android locationmanager kotlin kotlinx.coroutines
总的来说,我对 Kotlin 协程和 Android 开发很陌生。在尝试了解它是如何工作的时,我遇到了一个似乎无法解决的错误。
从基本活动中,我尝试连接到 googleApiClient。权限没问题。我希望使用 kotlin 协程以直接方式从 LocationManager 获取位置更新,以便稍后使用此 Location 对象。我第一次在模拟器中改变我的位置时它工作正常,第二次我改变我的位置时,它崩溃了,异常如下:
FATAL EXCEPTION: main
Process: com.link_value.eventlv, PID: 32404
java.lang.IllegalStateException: Already resumed, but got value Location[gps 48.783000,2.516180 acc=20 et=+59m16s372ms alt=0.0 {Bundle[mParcelledData.dataSize=40]}]
at kotlinx.coroutines.experimental.AbstractContinuation.resumeImpl(AbstractContinuation.kt:79)
at kotlinx.coroutines.experimental.AbstractContinuation.resume(AbstractContinuation.kt:72)
at com.link_value.eventlv.View.Create.NewEventLvActivity$await$2$1.onLocationChanged(NewEventLvActivity.kt:100)
at android.location.LocationManager$ListenerTransport._handleMessage(LocationManager.java:297)
at android.location.LocationManager$ListenerTransport.-wrap0(LocationManager.java)
at android.location.LocationManager$ListenerTransport$1.handleMessage(LocationManager.java:242)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new_event_lv)
askForUserLocation()
val locationManager = this.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val presenter = CreateEventPresenterImpl(this@NewEventLvActivity)
googleApiClient = GoogleApiClient.Builder(this@NewEventLvActivity)
.enableAutoManage(this /* FragmentActivity */,
this /* OnConnectionFailedListener */)
.addApi(Places.GEO_DATA_API)
.addConnectionCallbacks(this)
.build()
}
override fun onConnected(p0: Bundle?) {
val locationManager = this.getSystemService(Context.LOCATION_SERVICE) as LocationManager
input_address.addTextChangedListener(object: TextWatcher{
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(query: CharSequence?, p1: Int, p2: Int, p3: Int) {
if (query.toString().length >= 4) {
launch(UI) {
val locationUpdated = locationManager.await(LocationManager.GPS_PROVIDER)
input_name.text = Editable.Factory.getInstance().newEditable(locationUpdated.toString())
}
}
}
})
}
private suspend fun LocationManager.await(locationProvider: String): Location? = suspendCoroutine { cont ->
try {
requestLocationUpdates(locationProvider, 0, 0.toFloat(), object : LocationListener {
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) {
}
override fun onProviderEnabled(p0: String?) {
}
override fun onProviderDisabled(p0: String?) {
cont.resumeWithException(UnknownLocationException())
}
override fun onLocationChanged(location: Location?) {
cont.resume(location)
}
})
} catch (ex: SecurityException) {
cont.resumeWithException(ex)
}
}
Run Code Online (Sandbox Code Playgroud)
就好像 Kotlin 使用相同的 Continuation。我不知道我做错了什么以及为什么它第二次崩溃。有人可以启发我。提前谢谢。
Mik*_*rin 26
根据文档,可以仅恢复一次 Continuation。第二份简历将抛出IllegalStateException
“已经恢复,但收到 $proposedUpdate”消息。您可以添加额外的检查continuation.isActive
以防止出现此异常。最好将 Channels 用于多个回调,例如位置更新。
您可以使用此功能来防止此异常。
import kotlinx.coroutines.CancellableContinuation
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
inline fun <T> Continuation<T>.safeResume(value: T, onExceptionCalled: () -> Unit) {
if (this is CancellableContinuation) {
if (isActive)
resume(value)
else
onExceptionCalled()
} else throw Exception("Must use suspendCancellableCoroutine instead of suspendCoroutine")
}
Run Code Online (Sandbox Code Playgroud)
suspendCoroutine
请注意,如果您使用而不是 ,我将引发异常suspendCancellableCoroutine
。
用法:
suspend fun getId() : Result<String> = suspendCancellableCoroutine { continuation ->
...
continuation.safeResume(Result.success(adjustID)) { Timber.e("Job has already done") }
}
Run Code Online (Sandbox Code Playgroud)
我最近在我们的项目中使用的第 3 方 API 之一遇到了这个问题。问题仅在 API 方面,因为在底层实现中unsubscribe
多次调用回调,并且方法没有立即从 API 中删除回调。
所以,我选择的方法只是简单地添加一个布尔标志。虽然,它可以用 KotlinFlow.mapLatest/collectLatest
左右以更优雅的方式解决。
suspend fun singularContentUpdate(): Data =
suspendCancellableCoroutine { continuation ->
var resumed = false
val callback = object : Callback {
override fun onDataReady(d: Data) {
api.unsubscribe(this)
if (!resumed) {
continuation.resume(d)
resumed = true
}
}
}
continuation.invokeOnCancellation {
api.unsubscribe(callback)
}
api.subscribe(subscriber)
api.refresh()
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
9120 次 |
最近记录: |