Skip to content

Instantly share code, notes, and snippets.

@PasanBhanu
Last active July 25, 2023 13:28
Show Gist options
  • Star 41 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save PasanBhanu/730a32a9eeb180ec2950c172d54bb06a to your computer and use it in GitHub Desktop.
Save PasanBhanu/730a32a9eeb180ec2950c172d54bb06a to your computer and use it in GitHub Desktop.
Check Internet Connection in Android (API Level 29) Using Network Callback
/*
You need to call the below method once. It register the callback and fire it when there is a change in network state.
Here I used a Global Static Variable, So I can use it to access the network state in anyware of the application.
*/
// You need to pass the context when creating the class
public CheckNetwork(Context context) {
this.context = context;
}
// Network Check
public void registerNetworkCallback()
{
try {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest.Builder builder = new NetworkRequest.Builder();
connectivityManager.registerDefaultNetworkCallback(new ConnectivityManager.NetworkCallback(){
@Override
public void onAvailable(Network network) {
Variables.isNetworkConnected = true; // Global Static Variable
}
@Override
public void onLost(Network network) {
Variables.isNetworkConnected = false; // Global Static Variable
}
}
);
Variables.isNetworkConnected = false;
}catch (Exception e){
Variables.isNetworkConnected = false;
}
}
public class Variables {
// Global variable used to store network state
public static boolean isNetworkConnected = false;
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Register Callback - Call this in your app start!
CheckNetwork network = new CheckNetwork(getApplicationContext());
network.registerNetworkCallback();
// Check network connection
if (Variables.isNetworkConnected){
// Internet Connected
}else{
// Not Connected
}
}
}
@PasanBhanu
Copy link
Author

@iRYO400 Thanks for the contribution!

@chydee
Copy link

chydee commented May 19, 2020

Hi @iRYO400 @Abhinav1217 and @everyone ! This might be a bit out of the scope but I've gotta ask, after setting up a class or an activity that performs connectivity checks, whats the best ways to implement? For example here's how I normally do it (Alert! Boilerplates)
after setting up a public scope function or method in my Application class I then call the AppClass.isConnected() and do whatever I have to do or display a message. So my point is lets say I now do it the right way like @iRYO400 set up a ConnectivityMonitor.kt class, how do I set empty screens or show users messages on every part of the app to let them know they are offline (Keep in mind what I want do is reuse a single resource for this function across the project). I'd appreciate if you share any resource that shows how to do this the right way. Thank you.
maxresdefault

@iRYO400
Copy link

iRYO400 commented May 19, 2020

Hi @chydee !
I would recommend you check this example https://github.com/android10/Android-CleanArchitecture-Kotlin/blob/master/app/src/main/kotlin/com/fernandocejas/sample/core/platform/NetworkHandler.kt
Yes, it's a bit complicated for study, but it covers your case. Single place for network check and custom UI handling to show whether network is connected.

Also you'll need to refactor NetworkHandler, because it uses deprecated api. Check my example above

@chydee
Copy link

chydee commented May 20, 2020

Checking it out already. Thank you @iRYO400. Thank you so much for sharing

@GokhanArik
Copy link

GokhanArik commented Jul 13, 2020

TIL, if your phone is connected to a Network but the Network doesn't have a validated/working internet connection, it will give false result if you are only using NET_CAPABILITY_INTERNET. In addition, you also need to check for NET_CAPABILITY_VALIDATED. The fun part: It is only available after API 23.

@Abhinav1217
Copy link

TIL, if your phone is connected to a Network but the Network doesn't have a validated/working internet connection, it will give false result if you are only using NET_CAPABILITY_INTERNET. In addition, you also need to check for NET_CAPABILITY_VALIDATED. The fun part: It is only available after API 23.

This was the reason why NET_CAPABILITY_INTERNET was working unreliably as I mentioned before. It also fails when sometimes it takes long time for handshake between router and device. Thats when it will tell you you dont have connection when in truth, you just got a little longer to get validated.

registerDefaultNetworkCallback might be simple and rudimentary, but it works without hassle. And from that as a starting point, you can move on to more complex approach as needed by your app. The app I was working on, I have now implemented a timeout/retry logic for use cases where network can be patchy. So Its not just the register which is changing the value of global isNetworkConnected variable, But a retry logic which also changes that variable if more than 5 retries fails in a coroutine.

@waqqas7
Copy link

waqqas7 commented Sep 10, 2020

Thanks for the code. Works like a charm
Cheers mate !

@ExNDY
Copy link

ExNDY commented Oct 24, 2020

Hi,
I want to add my own version of this solution using LiveData. As I understand my solution targetting on API 24+ (delete parts deplecated solutions and not universal code other finding solutions). If u find mistakes or u know how to improve, please write about it. I was looking for a solution for a long time to replace Deplecated methods BroadcastReceiver. Thank You for best start!
https://gist.github.com/ExNDY/2c27b9c29b9642accbb0cd4f33ca9421

@TheCroid
Copy link

Hi,
I want to add my own version of this solution using LiveData. As I understand my solution targetting on API 24+ (delete parts deplecated solutions and not universal code other finding solutions). If u find mistakes or u know how to improve, please write about it. I was looking for a solution for a long time to replace Deplecated methods BroadcastReceiver. Thank You for best start!
https://gist.github.com/EndyMellman/2c27b9c29b9642accbb0cd4f33ca9421

@EndyMellman Thanks +1

@rohit-32
Copy link

                activeNetworks.removeAll { activeNetwork -> activeNetwork.networkHandle == network.networkHandle }
                val isNetworkConnected = activeNetworks.isNotEmpty()

You are a savior

@ayoubrem
Copy link

Hi
My own version for checking if the user already doesn't have a connection, also handle the connection change status
https://gist.github.com/ayoubrem/81689ca066f77ecd1ebb5e14de245e6d

@jisung-choi
Copy link

line 7 in CheckNetwork.java is throwing out error. I wanted to test if this code still works by directly copying/downloading, but failed to make it work.

@ayoubrem
Copy link

what error ?
the code work fine for me

@jisung-choi
Copy link

"class" or "interface" expected pops up for line7... It won't build. (I'm new to android so guide me please...)

@ayoubrem
Copy link

you have syntax error in the code
check semicolons & brackets

@jisung-choi
Copy link

I didn't change a thing from the original code. I downloaded Zip file, moved all the java files into Android Studio file
image

I just added the line with "package" on the first line like so
image

Had to change GlobalVariable file name to Variable b/c Android Studio made me so.

@jisung-choi
Copy link

Hi
My own version for checking if the user already doesn't have a connection, also handle the connection change status
https://gist.github.com/ayoubrem/81689ca066f77ecd1ebb5e14de245e6d

I tried your version && it works for me. So I think I will use your version for my app. :D

@huyhb
Copy link

huyhb commented Mar 4, 2021

NetworkRequest.Builder builder = new NetworkRequest.Builder(); 

what to do?

@Abhinav1217
Copy link

what to do?

You might find some examples here
You should also try official docs

@nishantkp
Copy link

If you just wanna check device is connected to WIFI or Cellular, you can try this...
https://gist.github.com/nishantkp/2ee7b8c34ea3e21cab1d64accd52078f

@baha2046a
Copy link

@Abhinav1217 I didn't notice you had a separate gist. Your gist is still missing an important piece that was pointed out on the response from the Google engineering team. That's where the fix from @gaucidaniel's code comes in.

My implementation is based on if the internet is available or not, regardless of it is Wifi or Mobile Data. If you want to know which network is active you need to modify the code.

There are 2 main issues with the connectivity check:

  1. onLost doesn't get called when you first open the app. onLost gets called if previously active Network loses the connection. However onAvailable gets called when the app connects to a Network. In addition to registering a callback, you also need to check the initial status connection status. To test this, turn on aeroplane mode and log onLost
  2. As they mentioned in the response, your device might be connected to 2 Networks at the same time. Thus onLost(Network)/onAvailable(Network)/onCapabilitiesChanged(Network) will be called for the given network. In @gaucidaniel's code, he keeps track of all the networks the app is connected to and checks if the list is empty before setting isNetworkConnected to false.
  • NET_CAPABILITY_INTERNET probably didn't work for the same reason. onCapabilitiesChanged(Network, NetworkCapabilities) gets called for a single network, you need to check if any of your networks have the capability. Setting a global variable to true/false will fail it.
  • The code in my gist is from my own project, I use RxRelay in my project, you don't need to use it.

To simplify further, here is a simple scenario. Assume you are connected to both Wifi and Mobile data.

Launch the app:

// onAvailable gets called twice for each network
onAvailable(Network) // Wifi
onAvailable(Network) // Cellular

Turn off the Wifi:

onLost(Network) // Called for Wifi only, but Cellular is still available. If you set your boolean to false, it will be inaccurate

According to this post.
Well, I think we don't need to keep check all different networks value.
In my guess, just change isNetworkConnected to an Int,
onAvailable -> +1
onLost -> -1
I have no prove but if the onAvailable and onLost is called properly it should work.

@baha2046a
Copy link

object CheckNetwork {
    var available:Int = 0
        private set

    fun registerNetworkCallback(context:Context) {
        kotlin.runCatching {
            val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?

            connectivityManager?.registerDefaultNetworkCallback(object : NetworkCallback() {
                override fun onAvailable(network: Network) {
                    available += 1
                    Log.d("CheckNetwork", "A $network")
                }

                override fun onLost(network: Network) {
                    available -= 1
                    Log.d("CheckNetwork", "L $network")
                }
            })
        }
    }
}

I just try it and seems it work
2021-03-26 18:14:52.901 2434-2816/com.example.tmdb D/CheckNetwork: A 140
2021-03-26 18:18:14.533 2434-2816/com.example.tmdb D/CheckNetwork: L 140
2021-03-26 18:18:21.915 2434-2816/com.example.tmdb D/CheckNetwork: A 141
2021-03-26 18:18:25.513 2434-2816/com.example.tmdb D/CheckNetwork: L 141
2021-03-26 18:18:30.613 2434-2816/com.example.tmdb D/CheckNetwork: A 142

@Abhinav1217
Copy link

@baha2046a

In my guess, just change isNetworkConnected to an Int,
onAvailable -> +1
onLost -> -1

This is a really bad strategy, These registers are triggered multiple times throughout app cycle. How will you determine the state, if there it was available 2 times but lost on third time, $available would be 1. And nonetheless, _if conditions_ needs a boolean. It will be either available or not available. Why waste cpu cycles comparing numeric equations to determine if condition is true or false, when you can just pass boolean directly. Android OS Apis provided those registers for a reason.

If you are wondering about the double calling during wifi->cellular switching, In real world scenario, there would be several edge cases handled when implementing it, so it should be covered. If I remember correctly, When network was switched, onLost was called by wifi, then onAvailable was triggered by cellular when it connected.

I have no prove but if the onAvailable and onLost is called properly it should work.

Without proof, nothing can be assured. Looking on your code, It won't work because you are assuming onAvailable and onLost to be called linearly which is never the case. I can't see any reason why you logged $network, and you haven't demonstrated how are you using available in your app. CheckNetwork object is just updating the value of available variable but how are you actually going to use it to determine state of your network. Publish your code on a separate gist and tag me there, I would be happy to take a look there. Lets keep this gist free.

Also, don't name your functions same as system functions, I got confused seeing at registerDefaultNetworkCallback being called inside your registerNetworkCallback function.

@baha2046a
Copy link

baha2046a commented Mar 26, 2021

@baha2046a

In my guess, just change isNetworkConnected to an Int,
onAvailable -> +1
onLost -> -1

This is a really bad strategy, These registers are triggered multiple times throughout app cycle. How will you determine the state, if there it was available 2 times but lost on third time, $available would be 1. And nonetheless, _if conditions_ needs a boolean. It will be either available or not available. Why waste cpu cycles comparing numeric equations to determine if condition is true or false, when you can just pass boolean directly. Android OS Apis provided those registers for a reason.

If you are wondering about the double calling during wifi->cellular switching, In real world scenario, there would be several edge cases handled when implementing it, so it should be covered. If I remember correctly, When network was switched, onLost was called by wifi, then onAvailable was triggered by cellular when it connected.

I have no prove but if the onAvailable and onLost is called properly it should work.

Without proof, nothing can be assured. Looking on your code, It won't work because you are assuming onAvailable and onLost to be called linearly which is never the case. I can't see any reason why you logged $network, and you haven't demonstrated how are you using available in your app. CheckNetwork object is just updating the value of available variable but how are you actually going to use it to determine state of your network. Publish your code on a separate gist and tag me there, I would be happy to take a look there. Lets keep this gist free.

Also, don't name your functions same as system functions, I got confused seeing at registerDefaultNetworkCallback being called inside your registerNetworkCallback function.

Naming is because I just modify the code from this thread.
For how to use the value, that is simple, when the value > 0 there has somehow a network connection exist.
As I know its a bitwise compare and I don't think there has a measurable performance different for true false.
The order of calling available and lost does not matter.
And I said this depend on they engineer the callback correctly (for example, when device switch on once only one onAvailable will call instead on dup calling, that is the part I cannot 100% prove).

Also, I notice a problem for the base design of the program from this thread.
Register the callback on onCreate may cause it add more then one times.
The callback work well at background but if the program get kill at background to free memory,
when it become active it will run onCreate again. I wonder that is the problem someone encountered.
So a better practice is
register at onStart, unregister at onStop
I found nothing wrong after I make this modification.

@Abhinav1217
Copy link

@baha2046a

Register the callback on onCreate may cause it add more then one times.

Tha'ts not whats happening in actual, Registration is done only done once, than a global static is set. As for the update of value, It is done by android system once registered ( something similar to pub-sub way, can't explain fully here ). So there is essentially no overhead. If you accidentally try to register it again, no effect takes place.
On other hand, the old way to initiate NetworkInfo, and then find out if network is available, That was something that was being done everytime check was asked for. Since Android 10, Google is Promoting pub-sub/callback/relay style of apis instead of old functional apis because of performance reason. Anything that needs to be queried from system should be subscribed for, thats the direction they seems to going for.

@CuongPTIT
Copy link

Hi
My own version for checking if the user already doesn't have a connection, also handle the connection change status
https://gist.github.com/ayoubrem/81689ca066f77ecd1ebb5e14de245e6d

hi bro, your link is page not found, Do you share again? Thank you

@sparsh820
Copy link

@SuppressLint("NewApi")
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void doWork() {

   ConnectivityManager cm=(ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);


    cm.registerDefaultNetworkCallback(new ConnectivityManager.NetworkCallback(){

        public void onAvailable(Network network) {
            Log.e("hola", "The default network is now: " + network);
             network=cm.getActiveNetwork();
              if(network!=null){
                  SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
                  String minMagnitude = sharedPrefs.getString(
                          getString(R.string.settings_min_magnitude_key),
                          getString(R.string.settings_min_magnitude_default));
                  String orderBy = sharedPrefs.getString(
                          getString(R.string.settings_order_by_key),
                          getString(R.string.settings_order_by_default)
                                                  );

                  if (orderBy.equals(getString(R.string.settings_order_by_default))){
                      extractdata(minMagnitude, "magnitude-asc");
                  }else{
                      extractdata(minMagnitude,orderBy);

                  }

              }
        }

        public void onCapabilitiesChanged(@NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
            super.onCapabilitiesChanged(network, networkCapabilities);
            Log.i(TAG,network+" "+networkCapabilities.toString()+" "+networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    View loadingIndicator = findViewById(R.id.progressBar);
                    loadingIndicator.setVisibility(View.VISIBLE);
                    rvCourses=findViewById(R.id.rvCourses);
                    mEmptyTextview=findViewById(R.id.empty_view);
                    mEmptyTextview.setVisibility(View.GONE);
                    rvCourses.setVisibility(View.VISIBLE);
                }
            });
            if(networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)){

                SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
                String minMagnitude = sharedPrefs.getString(
                        getString(R.string.settings_min_magnitude_key),
                        getString(R.string.settings_min_magnitude_default));
                String orderBy = sharedPrefs.getString(
                        getString(R.string.settings_order_by_key),
                        getString(R.string.settings_order_by_default)
                );

                if (orderBy.equals(getString(R.string.settings_order_by_default))){
                    extractdata(minMagnitude, "magnitude-asc");
                }else{
                    extractdata(minMagnitude,orderBy);

                }


            }
        }

           public void onLost(Network network) {
            /*View loadingIndicator = findViewById(R.id.progressBar);
            loadingIndicator.setVisibility(View.GONE);
            Log.e(TAG, "The application no longer has a default network. The last default network was " + network);
            rvCourses=findViewById(R.id.rvCourses);
            mEmptyTextview=findViewById(R.id.empty_view);
            mEmptyTextview.setText("NO INTERNET CONNECTION");
            rvCourses.setVisibility(View.GONE);
            mEmptyTextview.setVisibility(View.VISIBLE);*/

            runOnUiThread(new Runnable() {
                @Override
                public void run() {

                    View loadingIndicator = findViewById(R.id.progressBar);
                    loadingIndicator.setVisibility(View.GONE);
                    Log.e(TAG, "The application no longer has a default network. The last default network was " + network);
                    rvCourses=findViewById(R.id.rvCourses);
                    mEmptyTextview=findViewById(R.id.empty_view);
                    mEmptyTextview.setText("NO INTERNET CONNECTION");
                    rvCourses.setVisibility(View.GONE);
                    mEmptyTextview.setVisibility(View.VISIBLE);

                }
            });


        }





    }

    );


}

@sparsh820
Copy link

Hope this helps ;P

@mirsahib
Copy link

@PasanBhanu
please explain why you are using

        NetworkRequest.Builder builder = new NetworkRequest.Builder();

@Abhinav1217
Copy link

@PasanBhanu please explain why you are using

        NetworkRequest.Builder builder = new NetworkRequest.Builder();

Reminiscent of older code, Before we found about registerDefault from google team, older version of code was basically build a network request object, add the wifi module and/or bluetooth module (basically any and all network interface you want to observe) and then use that object to registerNetworkCallback

When you use registerDefaultNetworkCallback, it basically connects to the lower level network pool with default options, so you don't need to (but you still can if you want) build your own network object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment