In this codelab, you'll learn how to use GCM Network Manager to make battery optimized network calls.

What you'll learn

What you'll need

How will you use this tutorial?

Read it and complete the exercises Read it only

How would rate your experience with building Android apps?

Beginner Intermediate Expert

You can either download all the sample code to your computer...

Download Zip

...or clone the GitHub repository from the command line.

$ git clone https://github.com/googlecodelabs/android-network-manager.git

First, let's see what the finished sample app looks like. With the code downloaded, the following instructions that describe how to open the completed sample app in Android Studio.

  1. Select the network-manager directory from the sample code folder (Quickstart > Import Project... > network-manager).
  2. Click the Gradle sync button.
  3. Enable USB debugging on your Android device.
  4. Plug in your Android device and click the Run button. You should see the Network Manager main screen appear after a few seconds.
  5. Click on buttons "NOW" and "WHEN BEST", to trigger network requests.
  6. Verify that:
  1. With network connectivity, "NOW" requests are executed immediately and "WHEN BEST" requests are executed within about 30 seconds.
  2. Without network connectivity (turn Airplane Mode on), "NOW" requests fail and "WHEN BEST" requests remain pending and are executed when network connectivity returns (turn Airplane Mode off).

Frequently Asked Questions

Why?

You may be wondering why not use "NOW" style network calls all the time. This type of network call while sometimes appropriate does not optimize for battery life, network conditions or reboots. GCM Network Manager optimizes network calls for these and more important considerations.

Battery life is improved by making network calls in batches optimizing the cost of starting up the radios. If two or more of the same network calls are requested, GCM Network Manager can replace previous calls with the last one reducing the number of necessary calls. Since GCM Network Manager tasks are not run when the device is in "Doze" mode, this case is handled for you automatically.

If a task is scheduled at a time when the device does not have network connectivity, GCM Network Manager can hold on to that task and only execute it when network connectivity has returned.

When?

GCM Network Manager is designed to optimize network calls that are in the background, for example, app syncing as a result of a GCM sync tickle. It is not designed for network calls that result from user interaction like sending a chat message, or uploading a play in a game, in these cases use the "NOW" style of network call.

More Information:

Now you're ready to build on top of the starter project to add GCM Network Manager to it.

  1. Select the network-manager-start directory from your sample code download (File > Import Project... > network-manager-star).
  2. Click the Gradle sync button.
  3. Click the Run button.

You should see the Network Manager home screen appear after a few seconds.

Click on the "NOW" button and you will see Now Tasks being created and executed. Then click "WHEN BEST" button and you should see a Toast indicating that Oneoff Tasks are not yet implemented.

We'll implement Oneoff Tasks in the following steps.

First we need to add the GCM dependency that would provide the classes necessary for GCM Network Manager. Include the com.google.android.gcm:play-services-gcm:8.1.0 dependency in your dependency block.

build.gradle

dependencies {
   compile 'com.google.android.gms:play-services-gcm:8.1.0'
   compile 'com.android.support:design:23.1.0'
   compile 'com.android.support:recyclerview-v7:23.1.0'
   compile 'commons-io:commons-io:2.4'
   compile 'com.google.code.gson:gson:2.3.1'
}

The first step in implementing network calls with GCM Network Manager is to add a declaration of your implementation of GcmTaskService to the Android manifest. Let's do that now. Add a new servicce to the Application tag of your Android manifest file. Note that the name of the service is the name of your calss that extends GcmTaskService. BestTimeService in this case.

AndroidManifest.xml

<service android:name=".BestTimeService"
        android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE"
        android:exported="true">
   <intent-filter>
       <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY"/>
   </intent-filter>
</service>

With the service declared, you now need to add the code to the sample app that makes the network call when the task is run.

GcmTaskService provides all the cool network management, so to use it simply let one of your own classes extend it. In this case BestTimeService should extend GcmTaskService.

Update the BestTimeService task declaration to extend GcmTaskService, it should now look like:

BestTimeService.java

public class BestTimeService extends GcmTaskService {

Since BestTimeService.java extends GcmTaskService we must implement onRunTask the method. Add the onRunTask method, below, to BestTimeService.

BestTimeService.java

@Override
public int onRunTask(TaskParams taskParams) {
    String taskId = taskParams.getExtras().getString(CodelabUtil.TASK_ID);
    boolean completed = CodelabUtil.makeNetworkCall();

    Log.d(TAG, "Oneoff scheduled call executed. Task ID: " + taskId);

    // Prepare Intent to send with broadcast.
    Intent taskUpdateIntent = new Intent(CodelabUtil.TASK_UPDATE_FILTER);
    taskUpdateIntent.putExtra(CodelabUtil.TASK_ID, taskId);
    TaskItem taskItem = CodelabUtil.getTaskItemFromFile(this, taskId);
    if (taskItem == null) {
       return GcmNetworkManager.RESULT_FAILURE;
    }
    if (completed) {
       taskItem.setStatus(TaskItem.EXECUTED_STATUS);
    } else {
       taskItem.setStatus(TaskItem.FAILED_STATUS);
    }
    taskUpdateIntent.putExtra(CodelabUtil.TASK_STATUS, taskItem.getStatus());
    CodelabUtil.saveTaskItemToFile(this, taskItem);

    // Notify listeners (MainActivity) that task was completed successfully.
    LocalBroadcastManager localBroadcastManager =         
            LocalBroadcastManager.getInstance(this);
    localBroadcastManager.sendBroadcast(taskUpdateIntent);
    return GcmNetworkManager.RESULT_SUCCESS;
}

With the service declared in the manifest and having code to execute when the task is run, it is time to schedule the task. The task will be scheduled as a result of the user clicking on the "WHEN BEST" button.k

In MainActivity.java add a GcmNetworkManager. This object will be used to schedule tasks. Declare it at the top of MainActivity.java.

MainActivity.java

private GcmNetworkManager mGcmNetworkManager;

Then initialize the GcmNetworkManager in the onCreate method of MainActivity.java.

MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);

   mGcmNetworkManager = GcmNetworkManager.getInstance(this);

   ...
}

In MainActivity.java update the onPostExecute method of the AddTask AsyncTask. To include code that schedules the oneoff task. The resulting onPostExecute method should look like:

MainActivity.java

@Override
protected void onPostExecute(TaskItem taskItem) {
   mTaskAdapter.addTaskItem(taskItem);
   mRecyclerView.scrollToPosition(0);

   if (taskItem.getType().equals(TaskItem.ONEOFF_TASK)) {
       Bundle bundle = new Bundle();
       bundle.putString(CodelabUtil.TASK_ID, taskItem.getId());

       // Schedule oneoff task.
       OneoffTask oneoffTask = new OneoffTask.Builder()
               .setService(BestTimeService.class)
               .setTag(taskItem.getId())
               .setRequiredNetwork(OneoffTask.NETWORK_STATE_CONNECTED)
               // Use an execution window of 30 seconds or more. Less than 30
               // seconds would not allow GcmNetworkManager enough time to
               // optimize the next best time to execute your task.
               .setExecutionWindow(0, 30)
               .setExtras(bundle)
               .build();
       mGcmNetworkManager.schedule(oneoffTask);
   } else {
       // Immediately make network call.
       Intent nowIntent = new Intent(mContext, NowIntentService.class);
       nowIntent.putExtra(CodelabUtil.TASK_ID, taskItem.getId());
       mContext.startService(nowIntent);
   }
}

Note: setRequiredNetwork is used to require that the task be executed only when a network connection is available. Also a Bundle is used to pass parameters to the task service.

Now update the click listener of the "BEST TIME" button. In MainActivity update the code in the bestTimeButton click listener such that it look like:

MainActivity.java

bestTimeButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
       String taskId = TASK_ID_PREFIX + 
               Calendar.getInstance().getTimeInMillis();
       Log.d(TAG, "Scheduling oneoff task. " + taskId);
       TaskItem taskItem = new TaskItem(taskId, TaskItem.ONEOFF_TASK,
               TaskItem.PENDING_STATUS);
       new AddTask(view.getContext()).execute(taskItem);
   }
});

Now make sure that when you click "WHEN BEST" you are scheduling OneoffTasks that would execute within 30 seconds if there is an available network connection.

  1. Click the Gradle sync button.
  2. Click the Run button. You should see the home screen appear after a few seconds.
  1. Click "WHEN BEST" button to schedule OneoffTasks.
  2. Verify that if you put the device in Airplane Mode the scheduled OneoffTasks will not execute even after 30 seconds has elapsed. They will however execute when the connectivity is returned.

Your app is now ready to be a good Android networking citizen.

What we've covered

Next Steps

Learn More

Tell us how we did