【问题标题】:Kotlin coroutines resumeWithException errorKotlin 协程 resumeWithException 错误
【发布时间】:2020-09-11 23:54:58
【问题描述】:

我决定使用 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$1.onProviderDisabled(LocationUtils.kt:45)
        at android.location.LocationManager$ListenerTransport._handleMessage(LocationManager.java:384)
        at android.location.LocationManager$ListenerTransport.access$000(LocationManager.java:300)
        at android.location.LocationManager$ListenerTransport$1.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"))
    }
}

【问题讨论】:

  • log() 来自哪里?
  • 这是我的 BaseFragment 中的简单函数,它只是使用 Timber.d 和 Timber.e
  • 也许你的ProviderDisabledException 扩展了Throwable 而不是Exception
  • @AndreiTanana,不。 class ProviderDisabledException(provider: String) : Exception("Provider $provider disabled")

标签: android kotlin location coroutine continuations


【解决方案1】:

所以,我终于意识到为什么应用程序崩溃了 =)。使用协程一切正常。

这个方法有问题:

@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 style。所以要解决这个问题,我需要执行此方法挂起并抛出异常。 试图捕捉错误的地方:

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)
        }
    }

所以答案使函数downloadRestaurantsWithGPSLocationGoogledownloadRestaurantsWithFusedLocationGoogle 暂停并且不在其中启动单独的协程。 (删除lifecycleScope.launch

【讨论】:

    猜你喜欢
    • 2019-06-02
    • 2022-01-07
    • 1970-01-01
    • 2020-09-25
    • 2019-12-10
    • 1970-01-01
    • 2019-02-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多