自 Android 22 起,如何同步检索某些传输类型的网络状态?

How to synchronously retrieve network status for certain transport types as of Android 22?

我更改了问题标题,因为 解决了更广泛的问题,这使得关于包装回调的问题 API 对于特定问题来说是不必要的。原题为:

How to wrap asynchronous callback into suspend function using Coroutines?


我正在尝试将 Android 框架的 ConnectivityManager.NetworkCallback API(>= SDK 级别 21)封装到挂起函数中以促进同步 API:

private suspend fun ConnectivityManager.isNetworkAvailable(
    vararg transportType: /* android.net.NetworkCapabilities.Transport */ Int)
: Boolean {
    val isAvailable = suspendCancellableCoroutine<Boolean> { continuation ->
        val builder = NetworkRequest.Builder()
        transportType.forEach {
            builder.addCapability(it)
        }
        val networkRequest = builder.build()
        val networkCallback = object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network) {
                super.onAvailable(network)
                Log.d(javaClass.simpleName, "[${Thread.currentThread().name}] onAvailable")
                continuation.resume(true)
                unregisterNetworkCallback(this)
            }

            override fun onLost(network: Network) {
                super.onLost(network)
                Log.d(javaClass.simpleName, "[${Thread.currentThread().name}] onLost")
                continuation.resume(false)
                unregisterNetworkCallback(this)
            }
        }
        Log.d(javaClass.simpleName, "[${Thread.currentThread().name}] registerNetworkCallback BEFORE")
        registerNetworkCallback(networkRequest, networkCallback)
        Log.d(javaClass.simpleName, "[${Thread.currentThread().name}] registerNetworkCallback AFTER")
    }
    Log.d(javaClass.simpleName, "[${Thread.currentThread().name}] return isAvailable BEFORE")
    return isAvailable
}

当我调用 isNetworkAvailable(NetworkCapabilities.TRANSPORT_WIFI) 时,输出如下:

[DefaultDispatcher-worker-1] registerNetworkCallback BEFORE
[DefaultDispatcher-worker-1] registerNetworkCallback AFTER

不过,onAvailableonLost 从未被调用。

参考资料

相关

原来你想做的事情的相关 API 不是异步的,所以不需要协程:

private fun ConnectivityManager.isNetworkAvailable(
    vararg transportType: /* android.net.NetworkCapabilities.Transport */ Int)
: Boolean {
    val network = getActiveNetwork()
    val caps = getNetworkCapabilities(network)
    return caps != null && transportType.all(caps::hasTransport)
}

这只需要Manifest.permission.ACCESS_NETWORK_STATE.

如果您想主动搜索具有所需功能的网络,您将需要使用 requestNetwork API。 (和权限 Manifest.permission.CHANGE_NETWORK_STATE