Victor Cold
Victor Cold

Reputation: 653

ConnectivityManager.NetworkCallback. onLost called with delay

I have NetworkUtils to monitor the connection state:

object NetworkUtils {
    lateinit var connectivityManager: ConnectivityManager

    var isConnected = false; private set

    private object NetworkCallback : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            isConnected = true
        }

        override fun onLost(network: Network) {
            isConnected = false
        }
    }

    fun init(context: Context) {
        connectivityManager = context.getSystemService(ConnectivityManager::class.java)
    }

    fun isConnectedDeprecated(): Boolean {
        val networkInfo = connectivityManager.activeNetworkInfo
        return networkInfo?.isConnected == true
    }

    fun registerNetworkCallback() = connectivityManager.registerDefaultNetworkCallback(NetworkCallback)
    fun unregisterNetworkCallback() = connectivityManager.unregisterNetworkCallback(NetworkCallback)
}

And Interceptor I use with Retrofit:

class MyInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        return try {
            chain.proceed(chain.request())
        } catch (e: IOException) {
            throw if (NetworkUtils.isConnected()) {
                ExceptionA()
            } else {
                ExceptionB()
            }
        }
    }
}

The point is to know if IOException thrown from request caused by no connection (ExceptionB) or if it's some other network issue (ExceptionA).

The issue is if I turn off WIFI on my device in the middle of the request I expect to get ExceptionB, but sometimes I get ExceptionA. Because when interceptor catches IOException NetworkCallback's onLost isn't called yet.

I suspect that's because By default, the callback methods are called on the connectivity thread of your app, which is a separate thread used by ConnectivityManager. (link) And Retorfit runs interceptors on a different thread. So there's no any guaranteed order.

So is there a way to be sure that NetworkCallback will be hit before interceptor will catch the exception?

I know we can pass in Handler when registering the NetworkCallback, and maybe that could help us to somehow run NetworkCallback on the same thread as Retrofit interceptors. But I have no idea how to do it and it looks like a bit dirty solution.

Also, if check NetworkUtils.isConnectedDeprecated() in interceptor instead of NetworkUtils.isConnected then it works exactly like I want to. But documentation says: Deprecated. Apps should instead use the ConnectivityManager.NetworkCallback API to learn about connectivity changes. These will give a more accurate picture of the connectivity state of the device and let apps react more easily and quickly to changes.

So it's not more quickly if NetworkCallback is called with some delay, huh?

Upvotes: 1

Views: 663

Answers (0)

Related Questions