Detect Network State change using JobSchedulers in Android

12,153

Solution 1

Yes and no.

The JobInfo.Builder.setRequiredNetworkType() method allows you to schedule jobs to run when specific network conditions are met.

The network type can be one of three values:

  • JobInfo.NETWORK_TYPE_NONE: No network connectivity required.
  • JobInfo.NETWORK_TYPE_UNMETERED: An unmetered WiFi or Ethernet connection.
  • JobInfo.NETWORK_TYPE_ANY: Any network connection (WiFi or cellular).

Now, the catch... there's no NETWORK_TYPE_CELLUAR. You can't have your app only wake up when it's only on cellular. (Why would you want to do that?)

The other catch... WiFi connections can be metered or unmetered. Metered connections are typically things like mobile hotspots, and this can either be automatically detected (there's a special DHCP option the hotspot can send), or the user can manually toggle it on a per-network basis from WiFi Settings.

So, yes, you can set network-type constraints on your JobScheduler job. However, no, you don't get the level of granularity you're asking for.

As @CommonsWare mentioned, the idea is that you usually want to schedule network-related jobs to occur when network connectivity is unmetered, unless you have a good reason. (It's also a good idea to defer jobs until AC power is available, using setRequiresCharging(true), to conserve battery.)

Solution 2

This might not be the best solution. Please explain why before you down-vote this.

I used GcmTaskService to detect network state change using following code. It was for a weather app that I'm developing.

public class ServiceUpdateWeather extends GcmTaskService {

    private static final String TAG = ServiceUpdateWeather.class.getSimpleName();

    public static final String GCM_TAG_REPEAT_CONNECTIVITY_CHANGE = "UPDATE_WEATHER_CONNECTIVITY_CHANGE";

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public void onInitializeTasks() {
        //called when app is updated to a new version, reinstalled etc.
        //you have to schedule your repeating tasks again
        super.onInitializeTasks();
        if (Utilities.checkIsNougat()) {
            ServiceUpdateWeather.cancelConnectivityChange(getApplicationContext());
            ServiceUpdateWeather.scheduleConnectivityChange(getApplicationContext());
        }
    }

    @Override
    public int onRunTask(TaskParams taskParams) {
        Handler h = new Handler(getMainLooper());
        if(taskParams.getTag().equals(GCM_TAG_REPEAT_CONNECTIVITY_CHANGE)) {
            Log.i(TAG, "Connectivity changed task fired");
            h.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(ServiceUpdateWeather.this, "Updating weather", Toast.LENGTH_SHORT).show();
                    }
                });
            WeatherHelper.runNetworkConnectedUpdater(ServiceUpdateWeather.this);
        }

        return GcmNetworkManager.RESULT_SUCCESS;
    }

    public static void scheduleConnectivityChange(Context context) {
        try {
            PeriodicTask connectivityChange = new PeriodicTask.Builder()
                    //specify target service - must extend GcmTaskService
                    .setService(ServiceUpdateWeather.class)
                    //repeat every 30 seconds
                    .setPeriod(30)
                    //specify how much earlier the task can be executed (in seconds)
                    .setFlex(10)
                    //tag that is unique to this task (can be used to cancel task)
                    .setTag(GCM_TAG_REPEAT_CONNECTIVITY_CHANGE)
                    //whether the task persists after device reboot
                    .setPersisted(true)
                    //if another task with same tag is already scheduled, replace it with this task
                    .setUpdateCurrent(true)
                    //set required network state, this line is optional
                    .setRequiredNetwork(Task.NETWORK_STATE_CONNECTED)
                    //request that charging must be connected, this line is optional
                    .setRequiresCharging(false)
                    .build();
            GcmNetworkManager.getInstance(context).schedule(connectivityChange);
            Log.i(TAG, "Connectivity change task scheduled");
        } catch (Exception e) {
            Log.e(TAG, "Connectivity change task failed to schedule");
            e.printStackTrace();
        }
    }

    public static void cancelConnectivityChange(Context context) {
        GcmNetworkManager.getInstance(context).cancelTask(GCM_TAG_REPEAT_CONNECTIVITY_CHANGE, ServiceUpdateWeather.class);
        Log.v(TAG, "Connectivity change task cancelled");
    }
}

This seems to be working fine for me. It does not detect connectivity change immediately like the broadcast receiver does. But it runs for every 30 seconds if a network connection available. Make sure you call

if (Utilities.checkIsNougat()) {
    ServiceUpdateWeather.cancelConnectivityChange(getApplicationContext());
    ServiceUpdateWeather.scheduleConnectivityChange(getApplicationContext());
}

in your main activity onCreate method during first app launch to schedule this task for the first time.

Share:
12,153

Related videos on Youtube

Mrunal
Author by

Mrunal

Updated on June 18, 2022

Comments

  • Mrunal
    Mrunal about 2 years

    With Android N, You cannot statically register a Broadcast receivers for CONNECTIVITY_CHANGE intent.

    From http://developer.android.com/preview/features/background-optimization.html#connectivity-action Google documentation suggest using Job Schedulers to perform this task.

    Is it possible to detect network state change (LTE to wifi) and vice versa using Job Schedulers in Android?

    • CommonsWare
      CommonsWare about 8 years
      AFAIK, the idea isn't to detect the state change, but rather use JobScheduler to arrange to do some specific background work when you are on an unmetered connection, rather than attempt to model that using CONNECTIVITY_CHANGE broadcasts.
    • CommonsWare
      CommonsWare over 6 years
      @Merka: No, sorry.
  • Thirumalvalavan
    Thirumalvalavan about 7 years
    Hi Vajira, one small correction. GcmTaskService has backward compatibility. So no need to check Utilities.checkIsNougat(). I tested in Android 4.0 and 4.4. It is working & Thanks for this sample.
  • Vajira Lasantha
    Vajira Lasantha about 7 years
    @Thirumalvalavan True that. But in my app I used a regular broadcast receiver to run updater instantly on pre Nougat devices.
  • febaisi
    febaisi almost 7 years
    Well, there is a case that everyone would use the NETWORK_TYPE_CELLULAR. Let's say the user has NO SIM Card and the user is connected to Wi-Fi. If you want your app to be fired if the user switches the Wi-Fi to off, there is no way! After it gets disconnected of the Wi-Fi he doesn't go to the METERED network because he has NO Internet and there is no way to notify your app about that.