7

We use the Android ConnectivityManager to listen for internet connection changes inside our app as follows.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        ConnectionStateMonitor().enable(this)
    }

    class ConnectionStateMonitor : NetworkCallback() {
        private val networkRequest: NetworkRequest = NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build()

        fun enable(context: Context) {
            val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            connectivityManager.registerNetworkCallback(networkRequest, this)
        }

        override fun onAvailable(network: Network) {
            Log.i(TAG, "onAvailable ")
        }

        override fun onLost(network: Network?) {
            super.onLost(network)
            Log.i(TAG, "onLost ")
        }
    }
}

This implementation is working well except for two issues we've encountered

  1. If we connect to the internet by using both wifi and mobile data and turn off wifi sometimes the onLost() callback is fired followed by onAvailable(), as expected, but in other instances only onLost() is fired which is incorrect.

  2. If we don't have internet connection and open the app onLost() is not fired, however if we have internet connection and open the app onAvailable() is fired.

Any help, suggestions, workarounds or another approaches to detect internet connection changes reliably would really be appreciated.

Tested on Xioami A2 (Android 9), OnePlus (Android 9)

DEMO project
https://github.com/PhanVanLinh/AndroidNetworkChangeReceiver

the-ginger-geek
  • 7,041
  • 4
  • 27
  • 45
Linh
  • 57,942
  • 23
  • 262
  • 279

4 Answers4

3

I have used your project and I added another method: onCapabilitiesChanged. I started with the flight mode ebabled and then I turned off and then on again. These are the logs:

onAvailable 632

onCapabilitiesChanged 632 [ Transports: CELLULAR ...]

onAvailable 632

onCapabilitiesChanged 632 [ Transports: CELLULAR ...]

onAvailable 632

onCapabilitiesChanged 632 [ Transports: CELLULAR ...]

onCapabilitiesChanged 632 [ Transports: CELLULAR ...]

onCapabilitiesChanged 632 [ Transports: CELLULAR ...]

onAvailable 633

onCapabilitiesChanged 633 [ Transports: WIFI ...] onAvailable 633 onCapabilitiesChanged 633 [ Transports: WIFI ...]

onAvailable 633

onCapabilitiesChanged 633 [ Transports: WIFI ...]

onCapabilitiesChanged 633 [ Transports: WIFI ...]

onCapabilitiesChanged 633 [ Transports: WIFI ...]

onCapabilitiesChanged 633 [ Transports: WIFI ...]

onCapabilitiesChanged 633 [ Transports: WIFI ...]

onCapabilitiesChanged 633 [ Transports: WIFI ...]

onLost 632

onLost 632

onLost 632

onLost 633

onLost 633

onLost 633

onAvailable 634

onCapabilitiesChanged 634 [ Transports: CELLULAR ...]

onAvailable 634

onCapabilitiesChanged 634 [ Transports: CELLULAR ...]

onAvailable 634

onCapabilitiesChanged 634 [ Transports: CELLULAR ...]

onCapabilitiesChanged 634 [ Transports: CELLULAR ...]

onCapabilitiesChanged 634 [ Transports: CELLULAR ...]

onCapabilitiesChanged 634 [ Transports: CELLULAR ...]

onAvailable 635

onCapabilitiesChanged 635 [ Transports: WIFI ...]

onAvailable 635

onCapabilitiesChanged 635 [ Transports: WIFI ...]

onAvailable 635

onCapabilitiesChanged 635 [ Transports: WIFI ...]

onCapabilitiesChanged 635 [ Transports: WIFI ...]

onCapabilitiesChanged 635 [ Transports: WIFI ...]

onCapabilitiesChanged 635 [ Transports: WIFI ...]

onLost 634

onLost 634

onLost 634

onCapabilitiesChanged 635 [ Transports: WIFI ...]

onCapabilitiesChanged 635 [ Transports: WIFI ...]

As you can see the LOST is for the cellular transport while the AVAILABLE is for the WiFi

Following your use case (enable wifi, enable mobiledata, disable wifi data, enable wifi, disable wifi) this is what I get.

onAvailable 640

onCapabilitiesChanged 640 [ Transports: WIFI ... ]

onAvailable 640

onCapabilitiesChanged 640 [ Transports: WIFI ... ]

onCapabilitiesChanged 640 [ Transports: WIFI ... ]

onCapabilitiesChanged 640 [ Transports: WIFI ... ]

onCapabilitiesChanged 640 [ Transports: WIFI ... ]

onCapabilitiesChanged 640 [ Transports: WIFI ... ]

onLost 640

onLost 640

onAvailable 641

onCapabilitiesChanged 641 [ Transports: CELLULAR ... ]

onAvailable 641

onCapabilitiesChanged 641 [ Transports: CELLULAR ... ]

onCapabilitiesChanged 641 [ Transports: CELLULAR ... ]

onCapabilitiesChanged 641 [ Transports: CELLULAR ... ]

onAvailable 642

onCapabilitiesChanged 642 [ Transports: WIFI ... ]

onAvailable 642

onCapabilitiesChanged 642 [ Transports: WIFI ... ]

onCapabilitiesChanged 642 [ Transports: WIFI ... ]

onCapabilitiesChanged 642 [ Transports: WIFI ... ]

onCapabilitiesChanged 642 [ Transports: WIFI ... ]

onCapabilitiesChanged 642 [ Transports: WIFI ... ]

onLost 641

onLost 641

onLost 642

onLost 642

onAvailable 643

onCapabilitiesChanged 643 [ Transports: CELLULAR ... ]

onAvailable 643

onCapabilitiesChanged 643 [ Transports: CELLULAR ... ]

onCapabilitiesChanged 643 [ Transports: CELLULAR ... ]

onCapabilitiesChanged 643 [ Transports: CELLULAR ... ]

kingston
  • 11,053
  • 14
  • 62
  • 116
  • Thank you for your testing. I read your log 3 times but still can not understand. Currently, if you have android 9 device, you can test again few time like this `enable wifi, enable mobiledata, disable wifi data, enable wifi, disable wifi // normally onAvaiable not called here). You can try it few times and you will see problem. In my android 8 device, everything work well but I am not sure about all android 8 work well or not – Linh May 07 '19 at 01:21
  • It called onAvailable for the cellular network for me: every time I turned the Wifi off (even the last time). I'm using a Samsung Note 8 with Android 9. – kingston May 07 '19 at 07:35
  • Trying again I didn't get the onLost for the cellular when I turned the Wifi on, and then I didn't get the onAvailable when I turned it off. – kingston May 07 '19 at 07:49
  • `onCapabilitiesChanged ` should do the trick. I also had similar problem, and by adding check for valid internet `networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)` helped – Rafael Jul 27 '20 at 04:38
3

The callback should look like this:

val callback = object : ConnectivityManager.NetworkCallback() {

    private val availableNetworks: MutableSet<Network> = HashSet()

    override fun onAvailable(network: Network) {
        val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
        val hasInternetCapability =
            networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)

        if (hasInternetCapability == true) {
            availableNetworks.add(network)
            sendNetworkState()
        }
    }

    override fun onLost(network: Network) {
        availableNetworks.remove(network)
        sendNetworkState()
    }

    private fun sendNetworkState() {
        if (availableNetworks.isNotEmpty()) {
            // TODO: Notify the Internet connection is available
        } else {
            // TODO: Notify the Internet connection is unavailable
        }
    }
}

Registering the callback:

val request = NetworkRequest.Builder()
    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
    .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
    .build()

connectivityManager.registerNetworkCallback(networkRequest, callback)

Explanation

onLost is called per each network that was previously passed to onAvailable, according to the documentation:

If the callback was registered with registerNetworkCallback() it will be called for each network which no longer satisfies the criteria of the callback.

This also possible that the onAvailable will be called twice before the onLost

The code was originally found here: https://www.youtube.com/watch?v=To9aHYD5OVk

Mihail Ya
  • 361
  • 3
  • 6
1

Broadcast receiver definition on AndroidManifest has changed a bit. You can find appropriate description on here.

You have already implemented this way but anyhow I would like add Network callback class references as a reference point.

nurisezgin
  • 1,530
  • 12
  • 19
  • 1
    I read your source on github and found your main idea is checking internet status each 5s. If it is correct, I feel like it not very good for performance. Is there any way to optimization? Correct me if I am wrong. – Linh May 02 '19 at 01:58
  • Hi Linh, I can understand your concerns ok then I will update the context of answer may be you can find another solution on developer.android.com . – nurisezgin May 02 '19 at 12:19
  • Yes, actually listen internet connection change just a litle bit improvement for my application. If it consume too much battery I think I don't need to put it inside because it don't have too much benefit. Now I already remove this feature from my application :D, just when call API and there is no internet, I show error :v – Linh May 02 '19 at 13:22
1

Maybe you can try to use requestNetwork instead of registerNetworkCallback.

  • This wouldn't change anything related to onAvailable. The only difference in behavior between the two is that `requestNetwork` will keep a network up for your app while `registerNetworkCallback` will not. I.e., if no apps have open `requestNetwork` requests for a particular network, `ConnectivityService` will tear it down. It doesn't care about any `registerNetworkCallback` type requests however for this. – Always Learning Oct 25 '22 at 16:11