| Katie McCormick | 0ab9326 | 2013-11-01 18:10:51 -0700 | [diff] [blame] | 1 | page.title=Implementing GCM Client |
| kmccormick | f788ac1 | 2013-05-30 19:06:41 -0700 | [diff] [blame] | 2 | page.tags="cloud","push","messaging" |
| 3 | @jd:body |
| 4 | |
| 5 | <div id="qv-wrapper"> |
| 6 | <div id="qv"> |
| 7 | |
| Katie McCormick | 0ab9326 | 2013-11-01 18:10:51 -0700 | [diff] [blame] | 8 | |
| 9 | <h2>In this document</h2> |
| 10 | |
| 11 | <ol class="toc"> |
| 12 | <li><a href="#play-services">Set Up Google Play Services</a></li> |
| 13 | <li><a href="#manifest">Edit Your Application's Manifest</a></li> |
| 14 | <li><a href="#app">Write Your Application</a> |
| 15 | <ol class="toc"> |
| 16 | <li><a href="#sample-play">Check for Google Play Services APK</a></li> |
| 17 | <li><a href="#sample-register">Register for GCM</a></li> |
| 18 | <li><a href="#sample-send">Send a message</a></li> |
| 19 | <li><a href="#sample-receive">Receive a message</a></li> |
| 20 | </ol> |
| 21 | <li><a href="#run">Running the Sample</a></li> |
| 22 | <li><a href="#stats">Viewing Statistics</a></li> |
| 23 | </li> |
| 24 | |
| 25 | </ol> |
| 26 | |
| kmccormick | f788ac1 | 2013-05-30 19:06:41 -0700 | [diff] [blame] | 27 | <h2>See Also</h2> |
| 28 | |
| 29 | <ol class="toc"> |
| 30 | <li><a href="gs.html">Getting Started</a></li> |
| Katie McCormick | 0ab9326 | 2013-11-01 18:10:51 -0700 | [diff] [blame] | 31 | <li><a href="server.html">Implementing GCM Server</a></li> |
| kmccormick | f788ac1 | 2013-05-30 19:06:41 -0700 | [diff] [blame] | 32 | </ol> |
| 33 | |
| 34 | </div> |
| 35 | </div> |
| 36 | |
| Katie McCormick | 0ab9326 | 2013-11-01 18:10:51 -0700 | [diff] [blame] | 37 | <p>A GCM client is a GCM-enabled app that runs on an Android device. To write your |
| 38 | client code, we recommend that you use the |
| 39 | <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html"> |
| 40 | {@code GoogleCloudMessaging}</a> APIs. |
| 41 | The client helper library that was offered in previous versions of GCM still works, |
| 42 | but it has been superseded by the more efficient |
| 43 | <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html"> |
| 44 | {@code GoogleCloudMessaging}</a> APIs.</p> |
| kmccormick | f788ac1 | 2013-05-30 19:06:41 -0700 | [diff] [blame] | 45 | |
| Katie McCormick | 0ab9326 | 2013-11-01 18:10:51 -0700 | [diff] [blame] | 46 | <p>A full GCM implementation requires both a client implementation and a server |
| 47 | implementation. For more |
| 48 | information about implementing the server side, see <a href="server.html"> |
| 49 | Implementing GCM Server</a>.</p> |
| kmccormick | f788ac1 | 2013-05-30 19:06:41 -0700 | [diff] [blame] | 50 | |
| Katie McCormick | 0ab9326 | 2013-11-01 18:10:51 -0700 | [diff] [blame] | 51 | <p>The following sections walk you through the steps involved in writing a GCM |
| 52 | client-side application. Your client app can be arbitrarily complex, but at bare |
| 53 | minimum, a GCM client app must include code to register (and thereby get a |
| 54 | registration ID), and a broadcast receiver to receive messages sent by GCM. |
| 55 | </p> |
| 56 | |
| 57 | <h2 id="play-services">Step 1: Set Up Google Play Services</h2> |
| 58 | |
| 59 | <p>To write your client application, use the |
| 60 | <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html"> |
| 61 | {@code GoogleCloudMessaging}</a> API. |
| 62 | To use this API, you must set up your project to use the Google Play services SDK, |
| 63 | as described in <a href="/google/play-services/setup.html">Setup Google Play |
| 64 | Services SDK</a>.</p> |
| 65 | |
| 66 | <p class="note"><strong>Caution:</strong> When you add the Play Services library to |
| 67 | your project, be sure to add it <em>with resources</em>, as described in |
| 68 | <a href="{@docRoot}google/play-services/setup.html#Setup"> |
| 69 | Setup Google Play Services SDK</a>. The key point is that you must |
| 70 | <em>reference</em> the library—simply adding a {@code .jar} file to |
| 71 | your Eclipse project will not work. You must follow the directions |
| 72 | for referencing a library, or your app won't be able to access |
| 73 | the library's resources, and it won't run properly. |
| 74 | If you're using Android Studio, this is the string to add to the |
| 75 | {@code dependency} section of your application's {@code build.gradle} file:</p> |
| 76 | |
| 77 | <pre>dependencies { |
| Katie McCormick | 63b2b8c | 2014-02-07 17:36:03 -0800 | [diff] [blame] | 78 | compile "com.google.android.gms:play-services:3.1.+" |
| Katie McCormick | 0ab9326 | 2013-11-01 18:10:51 -0700 | [diff] [blame] | 79 | } |
| 80 | </pre> |
| 81 | |
| 82 | |
| 83 | <h2 id="manifest">Step 2: Edit Your Application's Manifest</h2> |
| 84 | |
| 85 | <p>Add the following to your application's manifest:</p> |
| 86 | <ul> |
| 87 | <li>The <code>com.google.android.c2dm.permission.RECEIVE</code> permission so |
| 88 | the Android application can register and receive messages.</li> |
| 89 | <li>The <code>android.permission.INTERNET</code> permission so the Android |
| 90 | application can send the registration ID to the 3rd party server.</li> |
| 91 | <li>The <code>android.permission.GET_ACCOUNTS</code> permission as GCM requires |
| 92 | a Google account (necessary only if if the device is running a version lower than |
| 93 | Android 4.0.4)</li> |
| 94 | <li>The <code>android.permission.WAKE_LOCK</code> permission so the application |
| 95 | can keep the processor from sleeping when a message is received. Optional—use |
| 96 | only if the app wants to keep the device from sleeping.</li> |
| 97 | <li>An <code>applicationPackage + ".permission.C2D_MESSAGE"</code> |
| 98 | permission to prevent other Android applications from registering and receiving |
| 99 | the Android application's messages. The permission name must exactly match this |
| 100 | pattern—otherwise the Android application will not receive the messages.</li> |
| 101 | <li>A receiver for <code>com.google.android.c2dm.intent.RECEIVE</code>, with |
| 102 | the category set |
| 103 | as <code>applicationPackage</code>. The receiver should require the |
| 104 | <code>com.google.android.c2dm.SEND</code> permission, so that only the GCM |
| 105 | Framework can send a message to it. If your app uses an {@link android.app.IntentService} |
| 106 | (not required, but a common pattern), this receiver should be an instance of |
| 107 | {@link android.support.v4.content.WakefulBroadcastReceiver}. |
| 108 | A {@link android.support.v4.content.WakefulBroadcastReceiver} takes care of |
| 109 | creating and managing a |
| 110 | <a href="{@docRoot}reference/android/os/PowerManager.html#PARTIAL_WAKE_LOCK"> |
| 111 | partial wake lock</a> for your app.</li> |
| 112 | |
| 113 | <li>A {@link android.app.Service} (typically an {@link android.app.IntentService}) |
| 114 | to which the {@link android.support.v4.content.WakefulBroadcastReceiver} passes off |
| 115 | the work of handling the GCM message, while ensuring that the device does not |
| 116 | go back to sleep in the process. Including an {@link android.app.IntentService} is |
| 117 | optional—you could choose to process your messages in a regular |
| 118 | {@link android.content.BroadcastReceiver} instead, but realistically, most apps will |
| 119 | use a {@link android.app.IntentService}. |
| 120 | </li> |
| 121 | <li>If the GCM feature is critical to the Android application's function, be sure to |
| 122 | set <code>android:minSdkVersion="8"</code> or higher in the manifest. This |
| 123 | ensures that the Android application cannot be installed in an environment in which it |
| 124 | could not run properly. </li> |
| 125 | </ul> |
| 126 | |
| 127 | <p>Here are excerpts from a sample manifest that supports GCM:</p> |
| 128 | |
| 129 | <pre class="prettyprint pretty-xml"> |
| 130 | <manifest package="com.example.gcm" ...> |
| 131 | |
| 132 | <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17"/> |
| 133 | <uses-permission android:name="android.permission.INTERNET" /> |
| 134 | <uses-permission android:name="android.permission.GET_ACCOUNTS" /> |
| 135 | <uses-permission android:name="android.permission.WAKE_LOCK" /> |
| 136 | <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> |
| 137 | |
| 138 | <permission android:name="com.example.gcm.permission.C2D_MESSAGE" |
| 139 | android:protectionLevel="signature" /> |
| 140 | <uses-permission android:name="com.example.gcm.permission.C2D_MESSAGE" /> |
| 141 | |
| 142 | <application ...> |
| 143 | <receiver |
| 144 | android:name=".GcmBroadcastReceiver" |
| 145 | android:permission="com.google.android.c2dm.permission.SEND" > |
| 146 | <intent-filter> |
| 147 | <action android:name="com.google.android.c2dm.intent.RECEIVE" /> |
| 148 | <category android:name="com.example.gcm" /> |
| 149 | </intent-filter> |
| 150 | </receiver> |
| 151 | <service android:name=".GcmIntentService" /> |
| 152 | </application> |
| 153 | |
| 154 | </manifest> |
| 155 | </pre> |
| 156 | |
| 157 | <h2 id="app"> Step 3: Write Your Application</h2> |
| 158 | |
| 159 | <p>Finally, write your application. This section features a sample client |
| 160 | application that illustrates how to use the |
| 161 | <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html"> |
| 162 | {@code GoogleCloudMessaging}</a> APIs. The sample consists of a main activity |
| 163 | ({@code DemoActivity}), a {@link android.support.v4.content.WakefulBroadcastReceiver} |
| 164 | ({@code GcmBroadcastReceiver}), and an {@link android.app.IntentService} |
| 165 | ({@code GcmIntentService}). You can find the complete source code for this sample at the |
| 166 | <a href="http://code.google.com/p/gcm">open source site</a>.</p> |
| 167 | |
| 168 | <p>Note the following:</p> |
| 169 | |
| 170 | <ul> |
| 171 | <li>Among other things, the sample illustrates registration and upstream |
| 172 | (device-to-cloud) messaging. Upstream messaging only applies to apps that are running against a |
| 173 | <a href="ccs.html">CCS</a> (XMPP) server; HTTP-based servers don't support upstream messaging.</li> |
| 174 | <li>The <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html"> |
| 175 | {@code GoogleCloudMessaging}</a> |
| 176 | registration APIs replace the old registration process, which was based on the |
| 177 | now-obsolete client helper library. While the old registration process still works, |
| 178 | we encourage you to use the newer |
| 179 | <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html"> |
| 180 | {@code GoogleCloudMessaging}</a> |
| 181 | registration APIs, regardless of your underlying server.</li> |
| 182 | </ul> |
| 183 | |
| 184 | <h3 id="sample-play">Check for Google Play Services APK</h3> |
| 185 | |
| 186 | <p>As described in <a href="{@docRoot}google/play-services/setup.html"> |
| 187 | Setup Google Play Services SDK</a>, apps that rely on the Play Services SDK |
| 188 | should always check the device for a compatible Google Play services APK before |
| 189 | accessing Google Play services features. In the sample app this check is done in |
| 190 | two places: in the main activity's {@code onCreate()} method, and in its |
| 191 | {@code onResume()} method. The check in {@code onCreate()} ensures that the app |
| 192 | can't be used without a successful check. The check in {@code onResume()} ensures |
| 193 | that if the user returns to the running app through some other means, such as |
| 194 | through the back button, the check is still performed. If the |
| 195 | device doesn't have a compatible Google Play services APK, your app can call |
| 196 | {@code GooglePlayServicesUtil.getErrorDialog()} to allow users to download the |
| 197 | APK from the Google Play Store or enable it in the device's system settings. |
| 198 | For example:</p> |
| 199 | |
| 200 | <pre>private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; |
| 201 | ... |
| 202 | @Override |
| 203 | public void onCreate(Bundle savedInstanceState) { |
| 204 | super.onCreate(savedInstanceState); |
| 205 | |
| 206 | setContentView(R.layout.main); |
| 207 | mDisplay = (TextView) findViewById(R.id.display); |
| 208 | |
| 209 | context = getApplicationContext(); |
| 210 | |
| 211 | // Check device for Play Services APK. |
| 212 | if (checkPlayServices()) { |
| 213 | // If this check succeeds, proceed with normal processing. |
| 214 | // Otherwise, prompt user to get valid Play Services APK. |
| 215 | ... |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | // You need to do the Play Services APK check here too. |
| 220 | @Override |
| 221 | protected void onResume() { |
| 222 | super.onResume(); |
| 223 | checkPlayServices(); |
| 224 | } |
| 225 | |
| 226 | /** |
| 227 | * Check the device to make sure it has the Google Play Services APK. If |
| 228 | * it doesn't, display a dialog that allows users to download the APK from |
| 229 | * the Google Play Store or enable it in the device's system settings. |
| 230 | */ |
| 231 | private boolean checkPlayServices() { |
| 232 | int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this); |
| 233 | if (resultCode != ConnectionResult.SUCCESS) { |
| 234 | if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) { |
| 235 | GooglePlayServicesUtil.getErrorDialog(resultCode, this, |
| 236 | PLAY_SERVICES_RESOLUTION_REQUEST).show(); |
| 237 | } else { |
| 238 | Log.i(TAG, "This device is not supported."); |
| 239 | finish(); |
| 240 | } |
| 241 | return false; |
| 242 | } |
| 243 | return true; |
| 244 | }</pre> |
| 245 | |
| 246 | <h3 id="sample-register">Register for GCM</h3> |
| 247 | <p>An Android application needs to register with GCM servers before it can receive |
| 248 | messages. When an app registers, it receives a registration ID, which it can then |
| 249 | store for future use. In the following snippet the {@code onCreate()} method in the sample app's |
| 250 | main activity checks to see if the app is already registered with GCM and with |
| 251 | the server:</p> |
| 252 | |
| 253 | <pre>/** |
| 254 | * Main UI for the demo app. |
| 255 | */ |
| 256 | public class DemoActivity extends Activity { |
| 257 | |
| 258 | public static final String EXTRA_MESSAGE = "message"; |
| 259 | public static final String PROPERTY_REG_ID = "registration_id"; |
| 260 | private static final String PROPERTY_APP_VERSION = "appVersion"; |
| 261 | private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; |
| 262 | |
| 263 | /** |
| 264 | * Substitute you own sender ID here. This is the project number you got |
| 265 | * from the API Console, as described in "Getting Started." |
| 266 | */ |
| 267 | String SENDER_ID = "Your-Sender-ID"; |
| 268 | |
| 269 | /** |
| 270 | * Tag used on log messages. |
| 271 | */ |
| 272 | static final String TAG = "GCMDemo"; |
| 273 | |
| 274 | TextView mDisplay; |
| 275 | GoogleCloudMessaging gcm; |
| 276 | AtomicInteger msgId = new AtomicInteger(); |
| 277 | SharedPreferences prefs; |
| 278 | Context context; |
| 279 | |
| 280 | String regid; |
| 281 | |
| 282 | @Override |
| 283 | public void onCreate(Bundle savedInstanceState) { |
| 284 | super.onCreate(savedInstanceState); |
| 285 | |
| 286 | setContentView(R.layout.main); |
| 287 | mDisplay = (TextView) findViewById(R.id.display); |
| 288 | |
| 289 | context = getApplicationContext(); |
| 290 | |
| 291 | // Check device for Play Services APK. If check succeeds, proceed with |
| 292 | // GCM registration. |
| 293 | if (checkPlayServices()) { |
| 294 | gcm = GoogleCloudMessaging.getInstance(this); |
| 295 | regid = getRegistrationId(context); |
| 296 | |
| 297 | if (regid.isEmpty()) { |
| 298 | registerInBackground(); |
| 299 | } |
| 300 | } else { |
| 301 | Log.i(TAG, "No valid Google Play Services APK found."); |
| 302 | } |
| 303 | } |
| 304 | ... |
| 305 | }</pre> |
| 306 | |
| 307 | <p>The app calls {@code getRegistrationId()} to see whether there is an existing |
| 308 | registration ID stored in shared preferences:</p> |
| 309 | |
| 310 | <pre>/** |
| 311 | * Gets the current registration ID for application on GCM service. |
| 312 | * <p> |
| 313 | * If result is empty, the app needs to register. |
| 314 | * |
| 315 | * @return registration ID, or empty string if there is no existing |
| 316 | * registration ID. |
| 317 | */ |
| 318 | private String getRegistrationId(Context context) { |
| 319 | final SharedPreferences prefs = getGCMPreferences(context); |
| 320 | String registrationId = prefs.getString(PROPERTY_REG_ID, ""); |
| 321 | if (registrationId.isEmpty()) { |
| 322 | Log.i(TAG, "Registration not found."); |
| 323 | return ""; |
| 324 | } |
| 325 | // Check if app was updated; if so, it must clear the registration ID |
| 326 | // since the existing regID is not guaranteed to work with the new |
| 327 | // app version. |
| 328 | int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE); |
| 329 | int currentVersion = getAppVersion(context); |
| 330 | if (registeredVersion != currentVersion) { |
| 331 | Log.i(TAG, "App version changed."); |
| 332 | return ""; |
| 333 | } |
| 334 | return registrationId; |
| 335 | } |
| 336 | ... |
| 337 | /** |
| 338 | * @return Application's {@code SharedPreferences}. |
| 339 | */ |
| 340 | private SharedPreferences getGCMPreferences(Context context) { |
| 341 | // This sample app persists the registration ID in shared preferences, but |
| 342 | // how you store the regID in your app is up to you. |
| 343 | return getSharedPreferences(DemoActivity.class.getSimpleName(), |
| 344 | Context.MODE_PRIVATE); |
| 345 | }</pre> |
| 346 | |
| 347 | <p>If the registration ID doesn't exist or the app was updated, |
| 348 | {@code getRegistrationId()} returns an empty string |
| 349 | to indicate that the app needs to get a new regID. {@code getRegistrationId()} calls |
| 350 | the following method to check the app version:</p> |
| 351 | |
| 352 | <pre>/** |
| 353 | * @return Application's version code from the {@code PackageManager}. |
| 354 | */ |
| 355 | private static int getAppVersion(Context context) { |
| 356 | try { |
| 357 | PackageInfo packageInfo = context.getPackageManager() |
| 358 | .getPackageInfo(context.getPackageName(), 0); |
| 359 | return packageInfo.versionCode; |
| 360 | } catch (NameNotFoundException e) { |
| 361 | // should never happen |
| 362 | throw new RuntimeException("Could not get package name: " + e); |
| 363 | } |
| 364 | }</pre> |
| 365 | |
| 366 | |
| 367 | <p>If there isn't a valid existing registration ID, {@code DemoActivity} calls the |
| 368 | following {@code registerInBackground()} method to register. Note that because the GCM |
| 369 | methods {@code register()} and {@code unregister()} are blocking, this has to |
| 370 | take place on a background thread. This sample uses {@link android.os.AsyncTask} |
| 371 | to accomplish this:</p> |
| 372 | |
| 373 | <pre> |
| 374 | /** |
| 375 | * Registers the application with GCM servers asynchronously. |
| 376 | * <p> |
| 377 | * Stores the registration ID and app versionCode in the application's |
| 378 | * shared preferences. |
| 379 | */ |
| 380 | private void registerInBackground() { |
| 381 | new AsyncTask<Void, Void, String>() { |
| 382 | @Override |
| 383 | protected String doInBackground(Void... params) { |
| 384 | String msg = ""; |
| 385 | try { |
| 386 | if (gcm == null) { |
| 387 | gcm = GoogleCloudMessaging.getInstance(context); |
| 388 | } |
| 389 | regid = gcm.register(SENDER_ID); |
| 390 | msg = "Device registered, registration ID=" + regid; |
| 391 | |
| 392 | // You should send the registration ID to your server over HTTP, |
| 393 | // so it can use GCM/HTTP or CCS to send messages to your app. |
| 394 | // The request to your server should be authenticated if your app |
| 395 | // is using accounts. |
| 396 | sendRegistrationIdToBackend(); |
| 397 | |
| 398 | // For this demo: we don't need to send it because the device |
| 399 | // will send upstream messages to a server that echo back the |
| 400 | // message using the 'from' address in the message. |
| 401 | |
| 402 | // Persist the regID - no need to register again. |
| 403 | storeRegistrationId(context, regid); |
| 404 | } catch (IOException ex) { |
| 405 | msg = "Error :" + ex.getMessage(); |
| 406 | // If there is an error, don't just keep trying to register. |
| 407 | // Require the user to click a button again, or perform |
| 408 | // exponential back-off. |
| 409 | } |
| 410 | return msg; |
| 411 | } |
| 412 | |
| 413 | @Override |
| 414 | protected void onPostExecute(String msg) { |
| 415 | mDisplay.append(msg + "\n"); |
| 416 | } |
| 417 | }.execute(null, null, null); |
| 418 | ... |
| Katie McCormick | 6b37262 | 2013-11-15 16:02:58 -0800 | [diff] [blame] | 419 | }</pre> |
| 420 | |
| 421 | <p>Once you've received your registration ID, send it to your server:</p> |
| 422 | <pre> |
| 423 | /** |
| 424 | * Sends the registration ID to your server over HTTP, so it can use GCM/HTTP |
| 425 | * or CCS to send messages to your app. Not needed for this demo since the |
| 426 | * device sends upstream messages to a server that echoes back the message |
| 427 | * using the 'from' address in the message. |
| 428 | */ |
| 429 | private void sendRegistrationIdToBackend() { |
| 430 | // Your implementation here. |
| Katie McCormick | 0ab9326 | 2013-11-01 18:10:51 -0700 | [diff] [blame] | 431 | }</pre> |
| 432 | |
| 433 | <p>After registering, the app calls {@code storeRegistrationId()} to store the |
| 434 | registration ID in shared preferences for future use. This is just one way of |
| 435 | persisting a regID. You might choose to use a different approach in your app:</p> |
| 436 | |
| 437 | <pre>/** |
| 438 | * Stores the registration ID and app versionCode in the application's |
| 439 | * {@code SharedPreferences}. |
| 440 | * |
| 441 | * @param context application's context. |
| 442 | * @param regId registration ID |
| 443 | */ |
| 444 | private void storeRegistrationId(Context context, String regId) { |
| 445 | final SharedPreferences prefs = getGCMPreferences(context); |
| 446 | int appVersion = getAppVersion(context); |
| 447 | Log.i(TAG, "Saving regId on app version " + appVersion); |
| 448 | SharedPreferences.Editor editor = prefs.edit(); |
| 449 | editor.putString(PROPERTY_REG_ID, regId); |
| 450 | editor.putInt(PROPERTY_APP_VERSION, appVersion); |
| 451 | editor.commit(); |
| 452 | }</pre> |
| 453 | |
| 454 | <h3 id="sample-send">Send a message</h3> |
| 455 | <p>When the user clicks the app's <strong>Send</strong> button, the app sends an |
| 456 | upstream message using the |
| 457 | <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html"> |
| 458 | {@code GoogleCloudMessaging}</a> APIs. In order to receive the upstream message, |
| 459 | your server should be connected to CCS. You can use one of the demo servers in |
| 460 | <a href="ccs.html#implement">Implementing an XMPP-based App Server</a> to run the sample and connect |
| 461 | to CCS.</p> |
| 462 | |
| 463 | <pre>public void onClick(final View view) { |
| 464 | if (view == findViewById(R.id.send)) { |
| 465 | new AsyncTask<Void, Void, String>() { |
| 466 | @Override |
| 467 | protected String doInBackground(Void... params) { |
| 468 | String msg = ""; |
| 469 | try { |
| 470 | Bundle data = new Bundle(); |
| 471 | data.putString("my_message", "Hello World"); |
| 472 | data.putString("my_action", |
| 473 | "com.google.android.gcm.demo.app.ECHO_NOW"); |
| 474 | String id = Integer.toString(msgId.incrementAndGet()); |
| 475 | gcm.send(SENDER_ID + "@gcm.googleapis.com", id, data); |
| 476 | msg = "Sent message"; |
| 477 | } catch (IOException ex) { |
| 478 | msg = "Error :" + ex.getMessage(); |
| 479 | } |
| 480 | return msg; |
| 481 | } |
| 482 | |
| 483 | @Override |
| 484 | protected void onPostExecute(String msg) { |
| 485 | mDisplay.append(msg + "\n"); |
| 486 | } |
| 487 | }.execute(null, null, null); |
| 488 | } else if (view == findViewById(R.id.clear)) { |
| 489 | mDisplay.setText(""); |
| 490 | } |
| 491 | }</pre> |
| 492 | |
| 493 | <h3 id="sample-receive">Receive a message</h3> |
| 494 | |
| 495 | <p>As described above in <a href="#manifest">Step 2</a>, the app includes a |
| 496 | {@link android.support.v4.content.WakefulBroadcastReceiver} for the <code>com.google.android.c2dm.intent.RECEIVE</code> |
| 497 | intent. A broadcast receiver is the mechanism GCM uses to deliver messages. When {@code onClick()} |
| 498 | calls {@code gcm.send()}, it triggers the broadcast receiver's {@code onReceive()} |
| 499 | method, which has the responsibility of making sure that the GCM message gets handled.</p> |
| 500 | <p>A {@link android.support.v4.content.WakefulBroadcastReceiver} is a special type of |
| 501 | broadcast receiver that takes care of |
| 502 | creating and managing a |
| 503 | <a href="{@docRoot}reference/android/os/PowerManager.html#PARTIAL_WAKE_LOCK"> |
| 504 | partial wake lock</a> for your app. |
| 505 | It passes off the work of processing the GCM message to a |
| 506 | {@link android.app.Service} (typically an |
| 507 | {@link android.app.IntentService}), while ensuring that the device does not |
| 508 | go back to sleep in the transition. If you don't hold a wake lock while transitioning |
| 509 | the work to a service, you are effectively allowing the device to go back to sleep before |
| 510 | the work completes. The net result is that the app might not finish processing |
| 511 | the GCM message until some arbitrary point in the future, which is not what you want.</p> |
| 512 | |
| 513 | <p class="note"><strong>Note:</strong> Using {@link android.support.v4.content.WakefulBroadcastReceiver} |
| 514 | is not a requirement. If you have a relatively simple app that doesn't require |
| 515 | a service, you can intercept the GCM message in a regular {@link android.content.BroadcastReceiver} |
| 516 | and do your processing there. Once you get the intent that GCM passes into |
| 517 | your broadcast receiver's {@code onReceive()} method, what you do with it |
| 518 | is up to you.</p> |
| 519 | |
| 520 | <p>This snippet starts {@code GcmIntentService} with the method |
| 521 | {@link android.support.v4.content.WakefulBroadcastReceiver#startWakefulService startWakefulService()}. |
| 522 | This method is comparable to {@link android.content.Context#startService startService()}, except that |
| 523 | the {@link android.support.v4.content.WakefulBroadcastReceiver} is holding a |
| 524 | wake lock when the service starts. The intent that is passed with |
| 525 | {@link android.support.v4.content.WakefulBroadcastReceiver#startWakefulService startWakefulService()} |
| 526 | holds an extra identifying the wake lock:</p> |
| 527 | |
| 528 | |
| 529 | <pre>public class GcmBroadcastReceiver extends WakefulBroadcastReceiver { |
| 530 | @Override |
| 531 | public void onReceive(Context context, Intent intent) { |
| 532 | // Explicitly specify that GcmIntentService will handle the intent. |
| 533 | ComponentName comp = new ComponentName(context.getPackageName(), |
| 534 | GcmIntentService.class.getName()); |
| 535 | // Start the service, keeping the device awake while it is launching. |
| 536 | startWakefulService(context, (intent.setComponent(comp))); |
| 537 | setResultCode(Activity.RESULT_OK); |
| 538 | } |
| 539 | }</pre> |
| 540 | |
| 541 | <p>The intent service shown below does the actual work of handling the GCM |
| 542 | message. When the service is finished, it calls |
| 543 | {@link android.support.v4.content.WakefulBroadcastReceiver#completeWakefulIntent GcmBroadcastReceiver.completeWakefulIntent()} |
| 544 | to release the wake lock. The |
| 545 | {@link android.support.v4.content.WakefulBroadcastReceiver#completeWakefulIntent completeWakefulIntent()} |
| 546 | method has as its parameter the same intent that was |
| 547 | passed in from the {@link android.support.v4.content.WakefulBroadcastReceiver}. |
| 548 | </p> |
| 549 | |
| 550 | <p>This snippet processes the GCM message based on message type, and posts the |
| 551 | result in a notification. But what you do with GCM messages in your app is up to |
| 552 | you—the possibilities are endless. For example, the message might be a ping, |
| 553 | telling the app to sync to a server to retrieve new content, or it might be a |
| 554 | chat message that you display in the UI.</p> |
| 555 | |
| 556 | <pre> |
| 557 | public class GcmIntentService extends IntentService { |
| 558 | public static final int NOTIFICATION_ID = 1; |
| 559 | private NotificationManager mNotificationManager; |
| 560 | NotificationCompat.Builder builder; |
| 561 | |
| 562 | public GcmIntentService() { |
| 563 | super("GcmIntentService"); |
| 564 | } |
| 565 | |
| 566 | @Override |
| 567 | protected void onHandleIntent(Intent intent) { |
| 568 | Bundle extras = intent.getExtras(); |
| 569 | GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this); |
| 570 | // The getMessageType() intent parameter must be the intent you received |
| 571 | // in your BroadcastReceiver. |
| 572 | String messageType = gcm.getMessageType(intent); |
| 573 | |
| 574 | if (!extras.isEmpty()) { // has effect of unparcelling Bundle |
| 575 | /* |
| 576 | * Filter messages based on message type. Since it is likely that GCM |
| 577 | * will be extended in the future with new message types, just ignore |
| 578 | * any message types you're not interested in, or that you don't |
| 579 | * recognize. |
| 580 | */ |
| 581 | if (GoogleCloudMessaging. |
| 582 | MESSAGE_TYPE_SEND_ERROR.equals(messageType)) { |
| 583 | sendNotification("Send error: " + extras.toString()); |
| 584 | } else if (GoogleCloudMessaging. |
| 585 | MESSAGE_TYPE_DELETED.equals(messageType)) { |
| 586 | sendNotification("Deleted messages on server: " + |
| 587 | extras.toString()); |
| 588 | // If it's a regular GCM message, do some work. |
| 589 | } else if (GoogleCloudMessaging. |
| 590 | MESSAGE_TYPE_MESSAGE.equals(messageType)) { |
| 591 | // This loop represents the service doing some work. |
| 592 | for (int i=0; i<5; i++) { |
| 593 | Log.i(TAG, "Working... " + (i+1) |
| 594 | + "/5 @ " + SystemClock.elapsedRealtime()); |
| 595 | try { |
| 596 | Thread.sleep(5000); |
| 597 | } catch (InterruptedException e) { |
| 598 | } |
| 599 | } |
| 600 | Log.i(TAG, "Completed work @ " + SystemClock.elapsedRealtime()); |
| 601 | // Post notification of received message. |
| 602 | sendNotification("Received: " + extras.toString()); |
| 603 | Log.i(TAG, "Received: " + extras.toString()); |
| 604 | } |
| 605 | } |
| 606 | // Release the wake lock provided by the WakefulBroadcastReceiver. |
| 607 | GcmBroadcastReceiver.completeWakefulIntent(intent); |
| 608 | } |
| 609 | |
| 610 | // Put the message into a notification and post it. |
| 611 | // This is just one simple example of what you might choose to do with |
| 612 | // a GCM message. |
| 613 | private void sendNotification(String msg) { |
| 614 | mNotificationManager = (NotificationManager) |
| 615 | this.getSystemService(Context.NOTIFICATION_SERVICE); |
| 616 | |
| 617 | PendingIntent contentIntent = PendingIntent.getActivity(this, 0, |
| 618 | new Intent(this, DemoActivity.class), 0); |
| 619 | |
| 620 | NotificationCompat.Builder mBuilder = |
| 621 | new NotificationCompat.Builder(this) |
| 622 | .setSmallIcon(R.drawable.ic_stat_gcm) |
| 623 | .setContentTitle("GCM Notification") |
| 624 | .setStyle(new NotificationCompat.BigTextStyle() |
| 625 | .bigText(msg)) |
| 626 | .setContentText(msg); |
| 627 | |
| 628 | mBuilder.setContentIntent(contentIntent); |
| 629 | mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build()); |
| 630 | } |
| 631 | }</pre> |
| 632 | |
| 633 | <h2 id="run">Running the Sample</h2> |
| 634 | |
| 635 | <p>To run the sample:</p> |
| 636 | |
| 637 | <ol> |
| 638 | <li>Follow the instructions in <a href="gs.html">Getting Started</a> to get your sender ID and |
| 639 | API key.</li> |
| 640 | <li>Implement your client app, as described in this document. You can find the complete source |
| 641 | code for the client app at the <a href="http://code.google.com/p/gcm">open source site</a>.</li> |
| 642 | <li>Run one of the demo servers (Java or Python) provided in |
| 643 | <a href="ccs.html#implement">Implementing an XMPP-based App Server</a>. Whichever demo server you |
| 644 | choose, don't forget to edit its code before running it to supply |
| 645 | your sender ID and API key. |
| 646 | </li> |
| 647 | |
| 648 | </ol> |
| 649 | |
| 650 | <h2 id="stats">Viewing Statistics</h2> |
| 651 | |
| 652 | <p>To view statistics and any error messages for your GCM applications:</p> |
| 653 | <ol> |
| 654 | <li> Go to the <code><a href="http://play.google.com/apps/publish">Developer Console</a></code>.</li> |
| 655 | <li>Login with your developer account. |
| 656 | <p>You will see a page that has a list of all of your apps.</p></li> |
| 657 | <li> Click on the "statistics" link next to the app for which you |
| 658 | want to view GCM stats. |
| 659 | <p>Now you are on the statistics page.</p> </li> |
| 660 | <li>Go to the drop-down menu and select the GCM metric you want to view. |
| 661 | </li> |
| 662 | </ol> |
| 663 | <p class="note"><strong>Note:</strong> Stats on the Google API Console are not |
| 664 | enabled for GCM. You must use the <a href="http://play.google.com/apps/publish">Developer Console</a>.</p> |
| kmccormick | f788ac1 | 2013-05-30 19:06:41 -0700 | [diff] [blame] | 665 | |