Kotlin协程resumeWithException错误
Kotlin coroutines resumeWithException error
我决定使用 kotlin coriutines 包装获取设备位置(一次,不更新),所以最后我得到了这个代码:
@SuppressLint("MissingPermission")
suspend fun LocationManager.getCurrentLocationOnce(): Location {
return suspendCancellableCoroutine { continuation ->
try {
val locationListener = object : SimpleLocationListener {
override fun onLocationChanged(location: Location?) {
if (location == null) {
this@getCurrentLocationOnce.removeUpdates(this)
continuation.resumeWithException(FailedToRetrieveLocationException("Location is NULL"))
} else {
this@getCurrentLocationOnce.removeUpdates(this)
continuation.resume(location)
}
}
override fun onProviderEnabled(provider: String?) {}
override fun onProviderDisabled(provider: String?) {
this@getCurrentLocationOnce.removeUpdates(this)
continuation.resumeWithException(ProviderDisabledException(provider ?: ""))
}
}
this.requestSingleUpdate(
LocationManager.GPS_PROVIDER,
locationListener,
null
)
} catch (e : Exception) {
continuation.resumeWithException(e)
}
}
}
当 GPS 开启时一切正常,但当 GPS 关闭时程序失败并出现异常 ProviderDisabledException
,那是因为:
override fun onProviderDisabled(provider: String?) {
this@getCurrentLocationOnce.removeUpdates(this)
continuation.resumeWithException(ProviderDisabledException(provider ?: ""))
}
但我不知道为什么会失败,因为在我使用这个函数的地方我得到了:
try {
val locationManager = (requireActivity().getSystemService(Context.LOCATION_SERVICE) as? LocationManager)
?: throw FailedToRetrieveLocationException("Location Service is null")
val location = locationManager.getCurrentLocationOnce()
log("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle",
"Successfully got location={lat:${location.latitude}, long:${location.longitude}}")
downloadRestaurantsWithLocation(location)
} catch (ex : FailedToRetrieveLocationException) {
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", ex)
throw ex
} catch (providerException : ProviderDisabledException) {
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", providerException)
throw providerException
} catch (e : Exception) {
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", e)
throw e
}
所以我正在记录异常并将其重新抛给调用函数,在调用函数中我捕获了这个异常:
try {
log("[GOOGLE] downloadRestaurants", "Starting donwload restaurants for GOOGLE")
downloadRestaurantsWithGPSLocationGoogle()
} catch (e : Exception) {
logError("[GOOGLE] error happened while getting location", e)
downloadRestaurantsWithFusedLocationGoogle()
}
在错误堆栈跟踪中,我只有这个:
E/[GOOGLE] downloadRestaurantsWithGPSLocationGoogle: my.package.location.exceptions.ProviderDisabledException: Provider gps disabled
at my.package.common.location.LocationUtilsKt$getCurrentLocationOnce$$inlined$suspendCancellableCoroutine$lambda.onProviderDisabled(LocationUtils.kt:45)
at android.location.LocationManager$ListenerTransport._handleMessage(LocationManager.java:384)
at android.location.LocationManager$ListenerTransport.access[=14=]0(LocationManager.java:300)
at android.location.LocationManager$ListenerTransport.handleMessage(LocationManager.java:316)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:207)
at android.app.ActivityThread.main(ActivityThread.java:6878)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:876)
我不知道为什么应用会失败,因为这样的代码可以完美运行:
lifecycleScope.launch {
try {
throwError()
} catch (e : Exception) {
e.printStackTrace()
}
}
private suspend fun throwError() {
return suspendCancellableCoroutine { continuation ->
continuation.resumeWithException(ProviderDisabledException("TEST"))
}
}
所以,我终于明白为什么它会导致应用程序崩溃 =)。协程一切正常。
这个方法有问题:
@Throws(ProviderDisabledException::class, FailedToRetrieveLocationException::class)
private fun downloadRestaurantsWithGPSLocationGoogle() = lifecycleScope.launch {
log("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", "Trying to get location via GPS")
try {
val locationManager = (requireActivity().getSystemService(Context.LOCATION_SERVICE) as? LocationManager)
?: throw FailedToRetrieveLocationException("Location Service is null")
val location = locationManager.getCurrentLocationOnce()
log("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle",
"Successfully got location={lat:${location.latitude}, long:${location.longitude}}")
downloadRestaurantsWithLocation(location)
} catch (ex : FailedToRetrieveLocationException) {
ex.printStackTrace()
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", ex)
throw ex
} catch (providerException : ProviderDisabledException) {
providerException.printStackTrace()
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", providerException)
throw providerException
} catch (e : Exception) {
e.printStackTrace()
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", e)
throw e
}
}
问题是我从协程中抛出异常并且不在协程中处理这个异常,所以我启动了我的协程并且跳过了所有的 try-cathces,因为这里我使用的是 fire and forget 风格.所以要解决这个问题,我需要执行此方法暂停并抛出异常。
尝试捕获错误的地方:
private fun downloadRestaurants() = lifecycleScope.launch {
log("downloadRestaurantsWithLocationSort",
"Requesting Manifest.permission.ACCESS_COARSE_LOCATION & Manifest.permission.ACCESS_FINE_LOCATION permissions")
val user = requestPermissions(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
if (!user.any { !it.second }) {
// permission is granted, can download restaurants and sort by nearest
log("downloadRestaurantsWithLocationSort", "Permissions is granted")
log("MANUFACTURER", Build.MANUFACTURER)
if (Build.MANUFACTURER == "Huawei" || Build.MANUFACTURER == "HUAWEI") {
showToast("HUAWEI")
try {
log("[HUAWEI] downloadRestaurants", "Starting donwload restaurants for HUAWEI")
downloadRestaurantsWithGPSLocationHuawei()
} catch (e : Exception) { // this will not work, because FIRE and FORGET
e.printStackTrace()
logError("[HUAWEI] error happened while getting location", e)
mainViewModel.downloadRestaurantsHeaders(null)
}
} else {
showToast("NOT A HUAWEI")
try {
log("[GOOGLE] downloadRestaurants", "Starting donwload restaurants for GOOGLE")
downloadRestaurantsWithGPSLocationGoogle()
} catch (e : Exception) { // this will not work, because FIRE and FORGET
e.printStackTrace()
logError("[GOOGLE] error happened while getting location", e)
downloadRestaurantsWithFusedLocationGoogle()
}
}
} else {
// permission is not granted, just download the restaurants
log("downloadRestaurantsWithLocationSort", "Permissions is NOT granted")
mainViewModel.downloadRestaurantsHeaders(null)
}
}
所以答案使函数 downloadRestaurantsWithGPSLocationGoogle
和 downloadRestaurantsWithFusedLocationGoogle
暂停并且不在它们内部启动单独的协程。 (删除lifecycleScope.launch
)
我决定使用 kotlin coriutines 包装获取设备位置(一次,不更新),所以最后我得到了这个代码:
@SuppressLint("MissingPermission")
suspend fun LocationManager.getCurrentLocationOnce(): Location {
return suspendCancellableCoroutine { continuation ->
try {
val locationListener = object : SimpleLocationListener {
override fun onLocationChanged(location: Location?) {
if (location == null) {
this@getCurrentLocationOnce.removeUpdates(this)
continuation.resumeWithException(FailedToRetrieveLocationException("Location is NULL"))
} else {
this@getCurrentLocationOnce.removeUpdates(this)
continuation.resume(location)
}
}
override fun onProviderEnabled(provider: String?) {}
override fun onProviderDisabled(provider: String?) {
this@getCurrentLocationOnce.removeUpdates(this)
continuation.resumeWithException(ProviderDisabledException(provider ?: ""))
}
}
this.requestSingleUpdate(
LocationManager.GPS_PROVIDER,
locationListener,
null
)
} catch (e : Exception) {
continuation.resumeWithException(e)
}
}
}
当 GPS 开启时一切正常,但当 GPS 关闭时程序失败并出现异常 ProviderDisabledException
,那是因为:
override fun onProviderDisabled(provider: String?) {
this@getCurrentLocationOnce.removeUpdates(this)
continuation.resumeWithException(ProviderDisabledException(provider ?: ""))
}
但我不知道为什么会失败,因为在我使用这个函数的地方我得到了:
try {
val locationManager = (requireActivity().getSystemService(Context.LOCATION_SERVICE) as? LocationManager)
?: throw FailedToRetrieveLocationException("Location Service is null")
val location = locationManager.getCurrentLocationOnce()
log("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle",
"Successfully got location={lat:${location.latitude}, long:${location.longitude}}")
downloadRestaurantsWithLocation(location)
} catch (ex : FailedToRetrieveLocationException) {
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", ex)
throw ex
} catch (providerException : ProviderDisabledException) {
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", providerException)
throw providerException
} catch (e : Exception) {
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", e)
throw e
}
所以我正在记录异常并将其重新抛给调用函数,在调用函数中我捕获了这个异常:
try {
log("[GOOGLE] downloadRestaurants", "Starting donwload restaurants for GOOGLE")
downloadRestaurantsWithGPSLocationGoogle()
} catch (e : Exception) {
logError("[GOOGLE] error happened while getting location", e)
downloadRestaurantsWithFusedLocationGoogle()
}
在错误堆栈跟踪中,我只有这个:
E/[GOOGLE] downloadRestaurantsWithGPSLocationGoogle: my.package.location.exceptions.ProviderDisabledException: Provider gps disabled
at my.package.common.location.LocationUtilsKt$getCurrentLocationOnce$$inlined$suspendCancellableCoroutine$lambda.onProviderDisabled(LocationUtils.kt:45)
at android.location.LocationManager$ListenerTransport._handleMessage(LocationManager.java:384)
at android.location.LocationManager$ListenerTransport.access[=14=]0(LocationManager.java:300)
at android.location.LocationManager$ListenerTransport.handleMessage(LocationManager.java:316)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:207)
at android.app.ActivityThread.main(ActivityThread.java:6878)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:876)
我不知道为什么应用会失败,因为这样的代码可以完美运行:
lifecycleScope.launch {
try {
throwError()
} catch (e : Exception) {
e.printStackTrace()
}
}
private suspend fun throwError() {
return suspendCancellableCoroutine { continuation ->
continuation.resumeWithException(ProviderDisabledException("TEST"))
}
}
所以,我终于明白为什么它会导致应用程序崩溃 =)。协程一切正常。
这个方法有问题:
@Throws(ProviderDisabledException::class, FailedToRetrieveLocationException::class)
private fun downloadRestaurantsWithGPSLocationGoogle() = lifecycleScope.launch {
log("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", "Trying to get location via GPS")
try {
val locationManager = (requireActivity().getSystemService(Context.LOCATION_SERVICE) as? LocationManager)
?: throw FailedToRetrieveLocationException("Location Service is null")
val location = locationManager.getCurrentLocationOnce()
log("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle",
"Successfully got location={lat:${location.latitude}, long:${location.longitude}}")
downloadRestaurantsWithLocation(location)
} catch (ex : FailedToRetrieveLocationException) {
ex.printStackTrace()
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", ex)
throw ex
} catch (providerException : ProviderDisabledException) {
providerException.printStackTrace()
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", providerException)
throw providerException
} catch (e : Exception) {
e.printStackTrace()
logError("[GOOGLE] downloadRestaurantsWithGPSLocationGoogle", e)
throw e
}
}
问题是我从协程中抛出异常并且不在协程中处理这个异常,所以我启动了我的协程并且跳过了所有的 try-cathces,因为这里我使用的是 fire and forget 风格.所以要解决这个问题,我需要执行此方法暂停并抛出异常。 尝试捕获错误的地方:
private fun downloadRestaurants() = lifecycleScope.launch {
log("downloadRestaurantsWithLocationSort",
"Requesting Manifest.permission.ACCESS_COARSE_LOCATION & Manifest.permission.ACCESS_FINE_LOCATION permissions")
val user = requestPermissions(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
if (!user.any { !it.second }) {
// permission is granted, can download restaurants and sort by nearest
log("downloadRestaurantsWithLocationSort", "Permissions is granted")
log("MANUFACTURER", Build.MANUFACTURER)
if (Build.MANUFACTURER == "Huawei" || Build.MANUFACTURER == "HUAWEI") {
showToast("HUAWEI")
try {
log("[HUAWEI] downloadRestaurants", "Starting donwload restaurants for HUAWEI")
downloadRestaurantsWithGPSLocationHuawei()
} catch (e : Exception) { // this will not work, because FIRE and FORGET
e.printStackTrace()
logError("[HUAWEI] error happened while getting location", e)
mainViewModel.downloadRestaurantsHeaders(null)
}
} else {
showToast("NOT A HUAWEI")
try {
log("[GOOGLE] downloadRestaurants", "Starting donwload restaurants for GOOGLE")
downloadRestaurantsWithGPSLocationGoogle()
} catch (e : Exception) { // this will not work, because FIRE and FORGET
e.printStackTrace()
logError("[GOOGLE] error happened while getting location", e)
downloadRestaurantsWithFusedLocationGoogle()
}
}
} else {
// permission is not granted, just download the restaurants
log("downloadRestaurantsWithLocationSort", "Permissions is NOT granted")
mainViewModel.downloadRestaurantsHeaders(null)
}
}
所以答案使函数 downloadRestaurantsWithGPSLocationGoogle
和 downloadRestaurantsWithFusedLocationGoogle
暂停并且不在它们内部启动单独的协程。 (删除lifecycleScope.launch
)