Internet Connection State in Android apps

Internet Connection State in Android apps


Internet connection sometimes can be crucial to your applications. You can inform a user with the banner stating that there is no internet connection, disconnect from the socket and restore a connection when the internet reappears, block HTTP/HTTPS requests, or even disable certain features.

laxmi kant wii

For that we will use Android SDK

What are we dealing with?

Android supports different types of network connections. It includes but is not limited to wifi, cellular, ethernet, and VPN connections.

Even you can have multiple network connections simultaneously, but only one of them will be active. That means if we have both cellular and wifi connections, Android chooses an active connection by prioritizing one of them.


We can disable the currently active network. Hence if there is another available network, Android needs some time to switch to that network.

Android devices can also go to sleep mode and disable all network operations.

So, suppose we want to implement a robust, reusable solution. In that case, the final result should check if the internet connection is active, notify users if the internet connection changes, and cover all possible edge cases.

Synchronous approach--->


To check if we currently have an active network connection, you can access the?activeNetworkInfo?in the?ConnectivityManager?class. It will look something like this-->

https://gist.github.com/codewith-fun/34fd1b97b2c94ac395d75d22f3ee7e12

https://gist.github.com/codewith-fun/34fd1b97b2c94ac395d75d22f3ee7e12


private val connectivityManager: ConnectivityManager = context.getSystemService()!
	

	val isNetworkConnected: Boolean
	    get() = connectivityManager.activeNetworkInfo?.isConnected ?: false!        

But the?NetworkInfo?and the?activeNetworkInfo?were deprecated in API level 29.

So, what is the preferred way to check if the active network is connected now?

activeNetworkInfo?was replaced with the?activeNetwork, which is the?Network?object. The?Network?object provides broader functionality, but most importantly, we can use it to replace the?NetworkInfo?approach to check synchronously if we currently have an active connection.

To do this, we need to retrieve the?NetworkCapabilities?from the active network and validate whether the?NetworkCapabilities?satisfy our requirements.

I used an extension in my example.


private val connectivityManager: ConnectivityManager = context.getSystemService()!
	

	    val isNetworkConnected: Boolean
	        get() = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
	            .isNetworkCapabilitiesValid()
	

	    private fun NetworkCapabilities?.isNetworkCapabilitiesValid(): Boolean = when {
	        this == null -> false
	        hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
	            hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) &&
	            (hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
	                hasTransport(NetworkCapabilities.TRANSPORT_VPN) ||
	                hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
	                hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) -> true
	        else -> false
	    }        

NetworkCapabilities.NET_CAPABILITY_INTERNET?means that Internet connectivity was successfully detected, while the?NetworkCapabilities.NET_CAPABILITY_VALIDATED?indicates that Android OS successfully validated connectivity on this network.

We should check if both capabilities are present to verify that we have a?Network?with an internet connection.

Listening to network state change

Since we have seen two synchronous approaches, we can’t use any of them to listen to the network state change events.

Luckily, Android SDK provides a variety of options to do that.

Two main functions are?registerNetworkCallback?and?registerDefaultNetworkCallback.

The essential difference between them is that the?registerDefaultNetworkCallback?listens only to the active network state.

At the same time, the?registerNetworkCallback?can be configured to listen to the specific networks, e.g., wifi connection, and also can listen to a couple of networks simultaneously.

Both functions accept?ConnectivityManager.NetworkCallback?as a parameter. There is a comprehensive explanation in the Android documentation on when each callback gets called. Recommended for reading -?link.

Here is an example of how you can use it:


private val networkCallback = ConnectivityManager.NetworkCallback() 
	        override fun onAvailable(network: Network) {
	            // Called when network is ready to use 
	        }
	

	        override fun onLost(network: Network) {
	            // Called when network disconnects
	        }
	

	        override fun onUnavailable() {
	            // Called if the requested network request cannot be fulfilled
	        }
	

	        override fun onCapabilitiesChanged(
	            network: Network,
	            networkCapabilities: NetworkCapabilities
	        ) {
	            // Called when the network corresponding to this request changes capabilities
	            // For example, "NetworkCapabilities.NET_CAPABILITY_VALIDATED" might appear with a delay.
	        }
	

	        override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
	            // Called when access to the specified network is blocked or unblocked.
	            // This function can be called, for example, when the device goes to the sleep mode.
	        }
	

	        override fun onLosing(network: Network, maxMsToLive: Int) {
	            // Called when the network is about to be lost
	        }
	

	        override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {
	            // Called when the network corresponding to this request changes LinkProperties
	        }
	    }
	

	    fun startListenNetworkState() {
	        connectivityManager.registerDefaultNetworkCallback(networkCallback)
	    }
	

	    fun stopListenNetworkState() {
	        connectivityManager.unregisterNetworkCallback(networkCallback)
	    }        

Here is my implementation of the internet connection state listener with Hilt and Kotlin coroutines.


interface NetworkConnectionManager 
	    /**
	     * Emits [Boolean] value when the current network becomes available or unavailable.
	     */
	    val isNetworkConnectedFlow: StateFlow<Boolean>
	

	    val isNetworkConnected: Boolean
	

	    fun startListenNetworkState()
	

	    fun stopListenNetworkState()
	}
	

	@Singleton
	class NetworkConnectionManagerImpl @Inject constructor(
	    @ApplicationContext context: Context,
	    coroutineScope: CoroutineScope
	) : NetworkConnectionManager {
	

	    private val connectivityManager: ConnectivityManager = context.getSystemService()!!
	

	    private val networkCallback = NetworkCallback()
	

	    private val _currentNetwork = MutableStateFlow(provideDefaultCurrentNetwork())
	

	    override val isNetworkConnectedFlow: StateFlow<Boolean> =
	        _currentNetwork
	            .map { it.isConnected() }
	            .stateIn(
	                scope = coroutineScope,
	                started = SharingStarted.WhileSubscribed(),
	                initialValue = _currentNetwork.value.isConnected()
	            )
	

	    override val isNetworkConnected: Boolean
	        get() = isNetworkConnectedFlow.value
	

	    override fun startListenNetworkState() {
	        if (_currentNetwork.value.isListening) {
	            return
	        }
	

	        // Reset state before start listening
	        _currentNetwork.update {
	            provideDefaultCurrentNetwork()
	                .copy(isListening = true)
	        }
	

	        connectivityManager.registerDefaultNetworkCallback(networkCallback)
	    }
	

	    override fun stopListenNetworkState() {
	        if (!_currentNetwork.value.isListening) {
	            return
	        }
	

	        _currentNetwork.update {
	            it.copy(isListening = false)
	        }
	

	        connectivityManager.unregisterNetworkCallback(networkCallback)
	    }
	

	    private inner class NetworkCallback : ConnectivityManager.NetworkCallback() {
	        override fun onAvailable(network: Network) {
	            _currentNetwork.update {
	                it.copy(isAvailable = true)
	            }
	        }
	

	        override fun onLost(network: Network) {
	            _currentNetwork.update {
	                it.copy(
	                    isAvailable = false,
	                    networkCapabilities = null
	                )
	            }
	        }
	

	        override fun onUnavailable() {
	            _currentNetwork.update {
	                it.copy(
	                    isAvailable = false,
	                    networkCapabilities = null
	                )
	            }
	        }
	

	        override fun onCapabilitiesChanged(
	            network: Network,
	            networkCapabilities: NetworkCapabilities
	        ) {
	            _currentNetwork.update {
	                it.copy(networkCapabilities = networkCapabilities)
	            }
	        }
	

	        override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
	            _currentNetwork.update {
	                it.copy(isBlocked = blocked)
	            }
	        }
	    }
	

	    /**
	     * On Android 9, [ConnectivityManager.NetworkCallback.onBlockedStatusChanged] is not called when
	     * we call the [ConnectivityManager.registerDefaultNetworkCallback] function.
	     * Hence we assume that the network is unblocked by default.
	     */
	    private fun provideDefaultCurrentNetwork(): CurrentNetwork {
	        return CurrentNetwork(
	            isListening = false,
	            networkCapabilities = null,
	            isAvailable = false,
	            isBlocked = false
	        )
	    }
	

	    private data class CurrentNetwork(
	        val isListening: Boolean,
	        val networkCapabilities: NetworkCapabilities?,
	        val isAvailable: Boolean,
	        val isBlocked: Boolean
	    )
	

	    private fun CurrentNetwork.isConnected(): Boolean {
	        // Since we don't know the network state if NetworkCallback is not registered.
	        // We assume that it's disconnected.
	        return isListening &&
	                isAvailable &&
	                !isBlocked &&
	                networkCapabilities.isNetworkCapabilitiesValid()
	    }
	

	    private fun NetworkCapabilities?.isNetworkCapabilitiesValid(): Boolean = when {
	        this == null -> false
	        hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
	                hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) &&
	                (hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
	                        hasTransport(NetworkCapabilities.TRANSPORT_VPN) ||
	                        hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
	                        hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) -> true
	        else -> false
	    }
	}{        

Just call the?startListenNetworkState()?function to start listening to the internet connection state.

For most use cases, it should be enough to call this function once during the application lifecycle. We could do it in the class constructor because we marked our class as a Singleton, like so:


init 
    
}
startListenNetworkState()
{        



Note:- registerDefaultNetworkCallback?could be replaced with the more modern?registerBestMatchingNetworkCallback?function added in API level 31.

要查看或添加评论,请登录

Laxmi Kant的更多文章

  • StateFlow vs SharedFlow in?kotlin

    StateFlow vs SharedFlow in?kotlin

    Before we Discuss about Stateflow vs SharedFlow let’s have look about the Kotlin flow ??? ???????? ???? ?? ????????? So…

  • @Composable Modifier vs composed factory

    @Composable Modifier vs composed factory

    Composed and CMF (@Composable Modifier Factory) are two methods to create custom modifiers in Jetpack Compose. They…

  • Android Context

    Android Context

    What is Context ? and how is it used? A Context is a handle to the system; It provides services like resolving…

  • Activity Launch Mode

    Activity Launch Mode

    Launch mode is an instruction for Android OS which specifies how the activity should be launched. It instructs how any…

    1 条评论

社区洞察

其他会员也浏览了