androidandroid-internet

how to determine internet access is not available on resuming android application


my current android application has a requirement to inform the user when internet access has been lost during a user session

i employ the following callback listener:-

@OptIn(DelicateCoroutinesApi::class)
class ConnectivityListener @Inject constructor() : CoroutineScope by GlobalScope, ConnectivityManager.NetworkCallback() {

    private var backgroundStateJob: Job? = null
    private val internalSharedWorkState = MutableSharedFlow<BackgroundState>(replay = 1, extraBufferCapacity = 7, onBufferOverflow = BufferOverflow.DROP_OLDEST)
    private val sharedWorkScheduleState: SharedFlow<BackgroundState> = internalSharedWorkState

    override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
        Timber.e( "onBlockedStatusChanged() called with: network = $network, blocked = $blocked")
    }
    override fun onLosing(network: Network, maxMsToLive: Int) {
        Timber.e( "onLosing() called with: network = $network, maxMsToLive = $maxMsToLive")
    }
    override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
        Timber.e( "onCapabilitiesChanged() called with: network = $network, networkCapabilities = $networkCapabilities")
    }
    override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {
        Timber.e( "onLinkPropertiesChanged() called with: network = $network, linkProperties = $linkProperties")
    }
    override fun onUnavailable() {
        Timber.e( "onUnavailable() called")
    }

    override fun onAvailable(network: Network) {
        Timber.e("onAvailable() called with: network = $network")
        backgroundStateJob?.cancel()
        backgroundStateJob = launch(Dispatchers.IO) {
            internalSharedWorkState.emit(BackgroundState.Connected())
        }
    }

    override fun onLost(network: Network) {
        Timber.e("onLost() called with: network = $network")
        backgroundStateJob?.cancel()
        backgroundStateJob = launch(Dispatchers.IO) {
            internalSharedWorkState.emit(BackgroundState.Disconnected())
        }
    }

    fun stateFlow(): SharedFlow<BackgroundState> {
        return sharedWorkScheduleState
    }
}

this listener is employed within my base activity as follow:-

private val networkRequest = NetworkRequest.Builder()
    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
    .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
    .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
    .build()

...

 override fun onResume() {
        Timber.e("onResume() called")
        super.onResume()
        val connectivityManager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        connectivityManager.registerNetworkCallback(networkRequest, connectivityListener)

    }

    override fun onPause() {
        Timber.e("onPause() called")
        super.onPause()
        val connectivityManager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        connectivityManager.unregisterNetworkCallback(connectivityListener)
    }

the listener works as expected while the application is in the foreground and reliable informs the user on the current network status whenever internet access is lost.

the listener also works fine when i exit the application, it goes into the background and is subsequently resumed. the listener successfully reports i have internet access.

however the listener does not work as required when i put the application into the background, enable airplane mode then resume the application.

it does not detect that there is no internet access, even though it does detect when internet access is available when the app is resumed from the background and airplane mode is not enabled.

what am i doing wrong? how can i detect the current network status when resuming my application from the background?


Solution

  • i managed to achieve the desired result by adding a second

    network callback as follows with a timeout by employing requestNetwork()

    override fun onResume() {
        super.onResume()
        val connectivityManager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        connectivityManager.registerNetworkCallback(networkRequest, connectivityListenerMonitor)
        connectivityManager.requestNetwork(networkRequest, connectivityListener, 1000)
    }
    
    override fun onPause() {
        super.onPause()
        val connectivityManager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        connectivityManager.unregisterNetworkCallback(connectivityListener)
        connectivityManager.unregisterNetworkCallback(connectivityListenerMonitor)
    }
    

    now when my application is in the background and i enable airplane mode then bring my application to the foreground my users are notified that there is no internet connection.