diff options
author | Alexander Lucas <alexlucas@google.com> | 2012-07-25 12:11:15 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-07-25 12:11:16 -0700 |
commit | 11ed0a1fbc6e30d5e8c72c19a4b88c8d35c2533f (patch) | |
tree | e75a0d86fa2fb6b6ca484e50655853c9b0507467 /docs | |
parent | b1e4a4f25f9fd4d1cee4629e71acb04e3d4e5ec0 (diff) | |
parent | e38a6c50ea6f6c1238276154ca0631842b6ab06c (diff) | |
download | frameworks_base-11ed0a1fbc6e30d5e8c72c19a4b88c8d35c2533f.zip frameworks_base-11ed0a1fbc6e30d5e8c72c19a4b88c8d35c2533f.tar.gz frameworks_base-11ed0a1fbc6e30d5e8c72c19a4b88c8d35c2533f.tar.bz2 |
Merge "Initial commit of GCM lesson for Cloudsync class." into jb-dev
Diffstat (limited to 'docs')
-rw-r--r-- | docs/html/training/cloudsync/aesync.jd | 432 | ||||
-rw-r--r-- | docs/html/training/cloudsync/backupapi.jd | 5 | ||||
-rw-r--r-- | docs/html/training/cloudsync/gcm.jd | 218 | ||||
-rw-r--r-- | docs/html/training/cloudsync/index.jd | 15 | ||||
-rw-r--r-- | docs/html/training/training_toc.cs | 8 |
5 files changed, 232 insertions, 446 deletions
diff --git a/docs/html/training/cloudsync/aesync.jd b/docs/html/training/cloudsync/aesync.jd deleted file mode 100644 index a21b429..0000000 --- a/docs/html/training/cloudsync/aesync.jd +++ /dev/null @@ -1,432 +0,0 @@ -page.title=Syncing with App Engine -parent.title=Syncing to the Cloud -parent.link=index.html - -trainingnavtop=true -next.title=Using the Backup API -next.link=backupapi.html - -@jd:body - -<div id="tb-wrapper"> -<div id="tb"> - -<!-- table of contents --> -<h2>This lesson teaches you how to</h2> -<ol> - <li><a href="#prepare">Prepare Your Environment</a></li> - <li><a href="#project">Create Your Project</a></li> - <li><a href="#data">Create the Data Layer</a></li> - <li><a href="#persistence">Create the Persistence Layer</a></li> - <li><a href="#androidapp">Query and Update from the Android App</a></li> - <li><a href="#serverc2dm">Configure the C2DM Server-Side</a></li> - <li><a href="#clientc2dm">Configure the C2DM Client-Side</a></li> -</ol> -<h2>You should also read</h2> - <ul> - <li><a - href="http://developers.google.com/appengine/">App Engine</a></li> - <li><a href="http://code.google.com/android/c2dm/">Android Cloud to Device - Messaging Framework</a></li> - </ul> -<h2>Try it out</h2> - -<p>This lesson uses the Cloud Tasks sample code, originally shown at the -<a href="http://www.youtube.com/watch?v=M7SxNNC429U">Android + AppEngine: A Developer's Dream Combination</a> -talk at Google I/O. You can use the sample application as a source of reusable code for your own -application, or simply as a reference for how the Android and cloud pieces of the overall -application fit together. You can also build the sample application and see how it runs -on your own device or emulator.</p> -<p> - <a href="http://code.google.com/p/cloud-tasks-io/" class="button">Cloud Tasks - App</a> -</p> - -</div> -</div> - -<p>Writing an app that syncs to the cloud can be a challenge. There are many little -details to get right, like server-side auth, client-side auth, a shared data -model, and an API. One way to make this much easier is to use the Google Plugin -for Eclipse, which handles a lot of the plumbing for you when building Android -and App Engine applications that talk to each other. This lesson walks you through building such a project.</p> - -<p>Following this lesson shows you how to:</p> -<ul> - <li>Build Android and Appengine apps that can communicate with each other</li> - <li>Take advantage of Cloud to Device Messaging (C2DM) so your Android app doesn't have to poll for updates</li> -</ul> - -<p>This lesson focuses on local development, and does not cover distribution -(i.e, pushing your App Engine app live, or publishing your Android App to -market), as those topics are covered extensively elsewhere.</p> - -<h2 id="prepare">Prepare Your Environment</h2> -<p>If you want to follow along with the code example in this lesson, you must do -the following to prepare your development environment:</p> -<ul> -<li>Install the <a href="http://code.google.com/eclipse/">Google Plugin for - Eclipse.</a></li> -<li>Install the <a - href="http://code.google.com/webtoolkit/download.html">GWT SDK</a> and the <a - href="http://code.google.com/appengine/">Java App Engine SDK</a>. The <a - href="http://code.google.com/eclipse/docs/getting_started.html">Quick Start - Guide</a> shows you how to install these components.</li> -<li>Sign up for <a href="http://code.google.com/android/c2dm/signup.html">C2DM - access</a>. We strongly recommend <a - href="https://accounts.google.com/SignUp">creating a new Google account</a> specifically for -connecting to C2DM. The server component in this lesson uses this <em>role - account</em> repeatedly to authenticate with Google servers. -</li> -</ul> - -<h2 id="project">Create Your Projects</h2> -<p>After installing the Google Plugin for Eclipse, notice that a new kind of Android project -exists when you create a new Eclipse project: The <strong>App Engine Connected - Android Project</strong> (under the <strong>Google</strong> project category). -A wizard guides you through creating this project, -during the course of which you are prompted to enter the account credentials for the role -account you created.</p> - -<p class="note"><strong>Note:</strong> Remember to enter the credentials for -your <i>role account</i> (the one you created to access C2DM services), not an -account you'd log into as a user, or as an admin.</p> - -<p>Once you're done, you'll see two projects waiting for you in your -workspace—An Android application and an App Engine application. Hooray! -These two applications are already fully functional— the wizard has -created a sample application which lets you authenticate to the App Engine -application from your Android device using AccountManager (no need to type in -your credentials), and an App Engine app that can send messages to any logged-in -device using C2DM. In order to spin up your application and take it for a test -drive, do the following:</p> - -<p>To spin up the Android application, make sure you have an AVD with a platform -version of <em>at least</em> Android 2.2 (API Level 8). Right click on the Android project in -Eclipse, and go to <strong>Debug As > Local App Engine Connected Android - Application</strong>. This launches the emulator in such a way that it can -test C2DM functionality (which typically works through Google Play). It'll -also launch a local instance of App Engine containing your awesome -application.</p> - -<h2 id="data">Create the Data Layer</h2> - -<p>At this point you have a fully functional sample application running. Now -it's time to start changing the code to create your own application.</p> - -<p>First, create the data model that defines the data shared between -the App Engine and Android applications. To start, open up the source folder of -your App Engine project, and navigate down to the <strong>(yourApp)-AppEngine - > src > (yourapp) > server</strong> package. Create a new class in there containing some data you want to -store server-side. The code ends up looking something like this:</p> -<pre> -package com.cloudtasks.server; - -import javax.persistence.*; - -@Entity -public class Task { - - private String emailAddress; - private String name; - private String userId; - private String note; - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - public Task() { - } - - public String getEmailAddress() { - return this.emailAddress; - } - - public Long getId() { - return this.id; - } - ... -} -</pre> -<p>Note the use of annotations: <code>Entity</code>, <code>Id</code> and -<code>GeneratedValue</code> are all part of the <a - href="http://www.oracle.com/technetwork/articles/javaee/jpa-137156.html">Java - Persistence API</a>. Essentially, the <code>Entity</code> annotation goes -above the class declaration, and indicates that this class represents an entity -in your data layer. The <code>Id</code> and <code>GeneratedValue</code> -annotations, respectively, indicate the field used as a lookup key for this -class, and how that id is generated (in this case, -<code>GenerationType.IDENTITY</code> indicates that the is generated by -the database). You can find more on this topic in the App Engine documentation, -on the page <a - href="http://code.google.com/appengine/docs/java/datastore/jpa/overview.html">Using - JPA with App Engine</a>.</p> - -<p>Once you've written all the classes that represent entities in your data -layer, you need a way for the Android and App Engine applications to communicate -about this data. This communication is enabled by creating a Remote Procedure -Call (RPC) service. -Typically, this involves a lot of monotonous code. Fortunately, there's an easy way! Right -click on the server project in your App Engine source folder, and in the context -menu, navigate to <strong>New > Other</strong> and then, in the resulting -screen, select <strong>Google > RPC Service.</strong> A wizard appears, pre-populated -with all the Entities you created in the previous step, -which it found by seeking out the <code>@Entity</code> annotation in the -source files you added. Pretty neat, right? Click <strong>Finish</strong>, and the wizard -creates a Service class with stub methods for the Create, Retrieve, Update and -Delete (CRUD) operations of all your entities.</p> - -<h2 id="persistence">Create the Persistence Layer</h2> - -<p>The persistence layer is where your application data is stored -long-term, so any information you want to keep for your users needs to go here. -You have several options for writing your persistence layer, depending on -what kind of data you want to store. A few of the options hosted by Google -(though you don't have to use these services) include <a - href="http://code.google.com/apis/storage/">Google Storage for Developers</a> -and App Engine's built-in <a - href="http://code.google.com/appengine/docs/java/gettingstarted/usingdatastore.html">Datastore</a>. -The sample code for this lesson uses DataStore code.</p> - -<p>Create a class in your <code>com.cloudtasks.server</code> package to handle -persistence layer input and output. In order to access the data store, use the <a - href="http://db.apache.org/jdo/api20/apidocs/javax/jdo/PersistenceManager.html">PersistenceManager</a> -class. You can generate an instance of this class using the PMF class in the -<code>com.google.android.c2dm.server.PMF</code> package, and then use that to -perform basic CRUD operations on your data store, like this:</p> -<pre> -/** -* Remove this object from the data store. -*/ -public void delete(Long id) { - PersistenceManager pm = PMF.get().getPersistenceManager(); - try { - Task item = pm.getObjectById(Task.class, id); - pm.deletePersistent(item); - } finally { - pm.close(); - } -} -</pre> - -<p>You can also use <a - href="http://code.google.com/appengine/docs/python/datastore/queryclass.html">Query</a> -objects to retrieve data from your Datastore. Here's an example of a method -that searches out an object by its ID.</p> - -<pre> -public Task find(Long id) { - if (id == null) { - return null; - } - - PersistenceManager pm = PMF.get().getPersistenceManager(); - try { - Query query = pm.newQuery("select from " + Task.class.getName() - + " where id==" + id.toString() + " && emailAddress=='" + getUserEmail() + "'"); - List<Task> list = (List<Task>) query.execute(); - return list.size() == 0 ? null : list.get(0); - } catch (RuntimeException e) { - System.out.println(e); - throw e; - } finally { - pm.close(); - } -} -</pre> - -<p>For a good example of a class that encapsulates the persistence layer for -you, check out the <a - href="http://code.google.com/p/cloud-tasks-io/source/browse/trunk/CloudTasks-AppEngine/src/com/cloudtasks/server/DataStore.java">DataStore</a> -class in the Cloud Tasks app.</p> - - - -<h2 id="androidapp">Query and Update from the Android App</h2> - -<p>In order to keep in sync with the App Engine application, your Android application -needs to know how to do two things: Pull data from the cloud, and send data up -to the cloud. Much of the plumbing for this is generated by the -plugin, but you need to wire it up to your Android user interface yourself.</p> - -<p>Pop open the source code for the main Activity in your project and look for -<code><YourProjectName> Activity.java</code>, then for the method -<code>setHelloWorldScreenContent()</code>. Obviously you're not building a -HelloWorld app, so delete this method entirely and replace it -with something relevant. However, the boilerplate code has some very important -characteristics. For one, the code that communicates with the cloud is wrapped -in an {@link android.os.AsyncTask} and therefore <em>not</em> hitting the -network on the UI thread. Also, it gives an easy template for how to access -the cloud in your own code, using the <a - href="http://code.google.com/webtoolkit/doc/latest/DevGuideRequestFactory.html">RequestFactory</a> -class generated that was auto-generated for you by the Eclipse plugin (called -MyRequestFactory in the example below), and various {@code Request} types.</p> - -<p>For instance, if your server-side data model included an object called {@code -Task} when you generated an RPC layer it automatically created a -{@code TaskRequest} class for you, as well as a {@code TaskProxy} representing the individual -task. In code, requesting a list of all these tasks from the server looks -like this:</p> - -<pre> -public void fetchTasks (Long id) { - // Request is wrapped in an AsyncTask to avoid making a network request - // on the UI thread. - new AsyncTask<Long, Void, List<TaskProxy>>() { - @Override - protected List<TaskProxy> doInBackground(Long... arguments) { - final List<TaskProxy> list = new ArrayList<TaskProxy>(); - MyRequestFactory factory = Util.getRequestFactory(mContext, - MyRequestFactory.class); - TaskRequest taskRequest = factory.taskNinjaRequest(); - - if (arguments.length == 0 || arguments[0] == -1) { - factory.taskRequest().queryTasks().fire(new Receiver<List<TaskProxy>>() { - @Override - public void onSuccess(List<TaskProxy> arg0) { - list.addAll(arg0); - } - }); - } else { - newTask = true; - factory.taskRequest().readTask(arguments[0]).fire(new Receiver<TaskProxy>() { - @Override - public void onSuccess(TaskProxy arg0) { - list.add(arg0); - } - }); - } - return list; - } - - @Override - protected void onPostExecute(List<TaskProxy> result) { - TaskNinjaActivity.this.dump(result); - } - - }.execute(id); -} -... - -public void dump (List<TaskProxy> tasks) { - for (TaskProxy task : tasks) { - Log.i("Task output", task.getName() + "\n" + task.getNote()); - } -} -</pre> - -<p>This {@link android.os.AsyncTask} returns a list of -<code>TaskProxy</code> objects, and sends it to the debug {@code dump()} method -upon completion. Note that if the argument list is empty, or the first argument -is a -1, all tasks are retrieved from the server. Otherwise, only the ones with -IDs in the supplied list are returned. All the fields you added to the task -entity when building out the App Engine application are available via get/set -methods in the <code>TaskProxy</code> class.</p> - -<p>In order to create new tasks and send them to the cloud, create a request -object and use it to create a proxy object. Then populate the proxy object and -call its update method. Once again, this should be done in an -<code>AsyncTask</code> to avoid doing networking on the UI thread. The end -result looks something like this.</p> - -<pre> -new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... arg0) { - MyRequestFactory factory = (MyRequestFactory) - Util.getRequestFactory(TasksActivity.this, - MyRequestFactory.class); - TaskRequest request = factory.taskRequest(); - - // Create your local proxy object, populate it - TaskProxy task = request.create(TaskProxy.class); - task.setName(taskName); - task.setNote(taskDetails); - task.setDueDate(dueDate); - - // To the cloud! - request.updateTask(task).fire(); - return null; - } -}.execute(); -</pre> - -<h2 id="serverc2dm">Configure the C2DM Server-Side</h2> - -<p>In order to set up C2DM messages to be sent to your Android device, go back -into your App Engine codebase, and open up the service class that was created -when you generated your RPC layer. If the name of your project is Foo, -this class is called FooService. Add a line to each of the methods for -adding, deleting, or updating data so that a C2DM message is sent to the -user's device. Here's an example of an update task: -</p> - -<pre> -public static Task updateTask(Task task) { - task.setEmailAddress(DataStore.getUserEmail()); - task = db.update(task); - DataStore.sendC2DMUpdate(TaskChange.UPDATE + TaskChange.SEPARATOR + task.getId()); - return task; -} - -// Helper method. Given a String, send it to the current user's device via C2DM. -public static void sendC2DMUpdate(String message) { - UserService userService = UserServiceFactory.getUserService(); - User user = userService.getCurrentUser(); - ServletContext context = RequestFactoryServlet.getThreadLocalRequest().getSession().getServletContext(); - SendMessage.sendMessage(context, user.getEmail(), message); -} -</pre> - -<p>In the following example, a helper class, {@code TaskChange}, has been created with a few -constants. Creating such a helper class makes managing the communication -between App Engine and Android apps much easier. Just create it in the shared -folder, define a few constants (flags for what kind of message you're sending -and a seperator is typically enough), and you're done. By way of example, -the above code works off of a {@code TaskChange} class defined as this:</p> - -<pre> -public class TaskChange { - public static String UPDATE = "Update"; - public static String DELETE = "Delete"; - public static String SEPARATOR = ":"; -} -</pre> - -<h2 id="clientc2dm">Configure the C2DM Client-Side</h2> - -<p>In order to define the Android applications behavior when a C2DM is recieved, -open up the <code>C2DMReceiver</code> class, and browse to the -<code>onMessage()</code> method. Tweak this method to update based on the content -of the message.</p> -<pre> -//In your C2DMReceiver class - -public void notifyListener(Intent intent) { - if (listener != null) { - Bundle extras = intent.getExtras(); - if (extras != null) { - String message = (String) extras.get("message"); - String[] messages = message.split(Pattern.quote(TaskChange.SEPARATOR)); - listener.onTaskUpdated(messages[0], Long.parseLong(messages[1])); - } - } -} -</pre> - -<pre> -// Elsewhere in your code, wherever it makes sense to perform local updates -public void onTasksUpdated(String messageType, Long id) { - if (messageType.equals(TaskChange.DELETE)) { - // Delete this task from your local data store - ... - } else { - // Call that monstrous Asynctask defined earlier. - fetchTasks(id); - } -} -</pre> -<p> -Once you have C2DM set up to trigger local updates, you're all done. -Congratulations, you have a cloud-connected Android application!</p> diff --git a/docs/html/training/cloudsync/backupapi.jd b/docs/html/training/cloudsync/backupapi.jd index 3055596..a5436c6 100644 --- a/docs/html/training/cloudsync/backupapi.jd +++ b/docs/html/training/cloudsync/backupapi.jd @@ -3,8 +3,9 @@ parent.title=Syncing to the Cloud parent.link=index.html trainingnavtop=true -previous.title=Syncing with App Engine -previous.link=aesync.html + +next.title=Making the Most of Google Cloud Messaging +next.link=gcm.html @jd:body diff --git a/docs/html/training/cloudsync/gcm.jd b/docs/html/training/cloudsync/gcm.jd new file mode 100644 index 0000000..dcc1b6b --- /dev/null +++ b/docs/html/training/cloudsync/gcm.jd @@ -0,0 +1,218 @@ +page.title=Making the Most of Google Cloud Messaging +parent.title=Syncing to the Cloud +parent.link=index.html + +trainingnavtop=true + +previous.title=Using the Backup API +previous.link=backupapi.html + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + <h2>This lesson teaches you to</h2> + <ol> + <li><a href="#multicast">Send Multicast Messages Efficiently</a></li> + <li><a href="#collapse">Collapse Messages that can Be Replaced</a></li> + <li><a href="#embed">Embed Data Directly in the GCM Message</a></li> + <li><a href="#react">React Intelligently to GCM Messages</a></li> + </ol> + <h2>You should also read</h2> + <ul> + <li><a href="http://developer.android.com/guide/google/gcm/index.html">Google + Cloud Messaging for Android</a></li> + </ul> + </div> +</div> + +<p>Google Cloud Messaging (GCM) is a free service for sending +messages to Android devices. GCM messaging can greatly enhance the user +experience. Your application can stay up to date without wasting battery power +on waking up the radio and polling the server when there are no updates. Also, +GCM allows you to attach up to 1,000 recipients to a single message, letting you easily contact +large user bases quickly when appropriate, while minimizing the work load on +your server.</p> + +<p>This lesson covers some of the best practices +for integrating GCM into your application, and assumes you are already familiar +with basic implementation of this service. If this is not the case, you can read the <a + href="http://developer.google.com/android/gcm/demo">GCM + Tutorial</a>.</p> + +<h2 id="multicast">Send Multicast Messages Efficiently</h2> +<p>One of the most useful features in GCM is support for up to 1,000 recipients for +a single message. This capability makes it much easier to send out important messages to +your entire user base. For instance, let's say you had a message that needed to +be sent to 1,000,000 of your users, and your server could handle sending out +about 500 messages per second. If you send each message with only a single +recipient, it would take 1,000,000/500 = 2,000 seconds, or around half an hour. +However, attaching 1,000 recipients to each message, the total time required to +send a message out to 1,000,000 recipients becomes (1,000,000/1,000) / 500 = 2 +seconds. This is not only useful, but important for timely data, such as natural +disaster alerts or sports scores, where a 30 minute interval might render the +information useless.</p> + +<p>Taking advantage of this functionality is easy. If you're using the <a + href="http://developer.android.com/guide/google/gcm/gs.html#libs">GCM helper + library</a> for Java, simply provide a <code>List<String></code> collection of +registration IDs to the <code>send</code> or <code>sendNoRetry</code> method, +instead of a single registration ID.</p> + +<pre> +// This method name is completely fabricated, but you get the idea. +List<String> regIds = whoShouldISendThisTo(message); + +// If you want the SDK to automatically retry a certain number of times, use the +// standard send method. +MulticastResult result = sender.send(message, regIds, 5); + +// Otherwise, use sendNoRetry. +MulticastResult result = sender.sendNoRetry(message, regIds); +</pre> + +<p>For those implementing GCM support in a language other than Java, construct +an HTTP POST request with the following headers:</p> +<ul> + <li><code>Authorization: key=YOUR_API_KEY</code></li> + <li><code>Content-type: application/json</code></li> +</ul> + +<p>Then encode the parameters you want into a JSON object, listing all the +registration IDs under the key <code>registration_ids</code>. The snippet below +serves as an example. All parameters except <code>registration_ids</code> are +optional, and the items nested in <code>data</code> represent the user-defined payload, not +GCM-defined parameters. The endpoint for this HTTP POST message will be +<code>https://android.googleapis.com/gcm/send</code>.</p> + +<pre> +{ "collapse_key": "score_update", + "time_to_live": 108, + "delay_while_idle": true, + "data": { + "score": "4 x 8", + "time": "15:16.2342" + }, + "registration_ids":["4", "8", "15", "16", "23", "42"] +} +</pre> + +<p>For a more thorough overview of the format of multicast GCM messages, see the <a + href="http://developer.android.com/guide/google/gcm/gcm.html#send-msg">Sending + Messages</a> section of the GCM guide.</pre> + +<h2 id="collapse">Collapse Messages that Can Be Replaced</h2> +<p>GCM messages are often a tickle, telling the mobile application to +contact the server for fresh data. In GCM, it's possible (and recommended) to +create collapsible messages for this situation, wherein new messages replace +older ones. Let's take the example +of sports scores. If you send out a message to all users following a certain +game with the updated score, and then 15 minutes later an updated score message +goes out, the earlier one no longer matters. For any users who haven't received +the first message yet, there's no reason to send both, and force the device to +react (and possibly alert the user) twice when only one of the messages is still +important.</p> + +<p>When you define a collapse key, when multiple messages are queued up in the GCM +servers for the same user, only the last one with any given collapse key is +delivered. For a situation like with sports scores, this saves the device from +doing needless work and potentially over-notifying the user. For situations +that involve a server sync (like checking email), this can cut down on the +number of syncs the device has to do. For instance, if there are 10 emails +waiting on the server, and ten "new email" GCM tickles have been sent to the +device, it only needs one, since it should only sync once.</p> + +<p>In order to use this feature, just add a collapse key to your outgoing +message. If you're using the GCM helper library, use the Message class's <code>collapseKey(String key)</code> method.</p> + +<pre> +Message message = new Message.Builder(regId) + .collapseKey("game4_scores") // The key for game 4. + .ttl(600) // Time in seconds to keep message queued if device offline. + .delayWhileIdle(true) // Wait for device to become active before sending. + .addPayload("key1", "value1") + .addPayload("key2", "value2") + .build(); +</pre> + +<p>If not using the helper library, simply add a variable to the +POST header you're constructing, with <code>collapse_key</code> as the field +name, and the string you're using for that set of updates as the value.</p> + + + +<h2 id="embed">Embed Data Directly in the GCM Message</h2> +<p>Often, GCM messages are meant to be a tickle, or indication to the device +that there's fresh data waiting on a server somewhere. However, a GCM message +can be up to 4kb in size, so sometimes it makes sense to simply send the +data within the GCM message itself, so that the device doesn't need to contact the +server at all. Consider this approach for situations where all of the +following statements are true: +<ul> + <li>The total data fits inside the 4kb limit.</li> + <li>Each message is important, and should be preserved.</li> + <li>It doesn't make sense to collapse multiple GCM messages into a single + "new data on the server" tickle.</li> +</ul> + +<p>For instance, short messages or encoded player moves +in a turn-based network game are examples of good use-cases for data to embed directly +into a GCM message. Email is an example of a bad use-case, since messages are +often larger than 4kb, +and users don't need a GCM message for each email waiting for them on +the server.</p> + +<p>Also consider this approach when sending +multicast messages, so you don't tell every device across your user base to hit +your server for updates simultaneously.</p> +<p>This strategy isn't appropriate for sending large amounts of data, for a few +reasons:</p> +<ul> + <li>Rate limits are in place to prevent malicious or poorly coded apps from spamming an + individual device with messages.</li> + <li>Messages aren't guaranteed to arrive in-order.</li> + <li>Messages aren't guaranteed to arrive as fast as you send them out. Even + if the device receives one GCM message a second, at a max of 1K, that's 8kbps, or + about the speed of home dial-up internet in the early 1990's. Your app rating + on Google Play will reflect having done that to your users.</p> +</ul> + +<p>When used appropriately, directly embedding data in the GCM message can speed +up the perceived speediness of your application, by letting it skip a round trip +to the server.</p> + +<h2 id="react">React Intelligently to GCM Messages</h2> +<p>Your application should not only react to incoming GCM messages, but react +<em>intelligently</em>. How to react depends on the context.</p> + +<h3>Don't be irritating</h3> +<p>When it comes to alerting your user of fresh data, it's easy to cross the line +from "useful" to "annoying". If your application uses status bar notifications, +<a + href="http://developer.android.com/guide/topics/ui/notifiers/notifications.html#Updating">update + your existing notification</a> instead of creating a second one. If you +beep or vibrate to alert the user, consider setting up a timer. Don't let the +application alert more than once a minute, lest users be tempted to uninstall +your application, turn the device off, or toss it in a nearby river.</p> + +<h3>Sync smarter, not harder</h3> +<p>When using GCM as an indicator to the device that data needs to be downloaded +from the server, remember you have 4kb of metadata you can send along to +help your application be smart about it. For instance, if you have a feed +reading app, and your user has 100 feeds that they follow, help the device be +smart about what it downloads from the server! Look at the following examples +of what metadata is sent to your application in the GCM payload, and how the application +can react:</p> +<ul> + <li><code>refresh</code> — Your app basically got told to request a dump of + every feed it follows. Your app would either need to send feed requests to 100 different servers, or + if you have an aggregator on your server, send a request to retrieve, bundle + and + transmit recent data from 100 different feeds, every time one updates.</li> + <li><code>refresh</code>, <code>feedID</code> — Better: Your app knows to check + a specific feed for updates.</li> + <li><code>refresh</code>, <code>feedID</code>, <code>timestamp</code> — + Best: If the user happened to manually refresh before the GCM message + arrived, the application can compare timestamps of the most recent post, and + determine that it <em>doesn't need to do anything</em>. +</ul> diff --git a/docs/html/training/cloudsync/index.jd b/docs/html/training/cloudsync/index.jd index e53844b..91885e8 100644 --- a/docs/html/training/cloudsync/index.jd +++ b/docs/html/training/cloudsync/index.jd @@ -2,8 +2,8 @@ page.title=Syncing to the Cloud trainingnavtop=true startpage=true -next.title=Syncing with App Engine -next.link=aesync.html +next.title=Making the Most of Google Cloud Messaging +next.link=gcm.html @jd:body @@ -21,14 +21,13 @@ installing your application on a new device. <h2>Lessons</h2> <dl> - <dt><strong><a href="aesync.html">Syncing with App Engine.</a></strong></dt> - <dd>Learn how to create a paired App Engine app and Android app which share a - data model, authenticates using the AccountManager, and communicate with each - other via REST and C2DM.</dd> - <dt><strong><a href="backupapi.html">Using the Backup - API</a></strong></dt> + <dt><strong><a href="backupapi.html">Using the Backup API</a></strong></dt> <dd>Learn how to integrate the Backup API into your Android Application, so that user data such as preferences, notes, and high scores update seamlessly across all of a user's devices</dd> + <dt><strong><a href="gcm.html">Making the Most of Google Cloud Messaging</a></strong></dt> + <dd>Learn how to efficiently send multicast messages, react intelligently to + incoming Google Cloud Messaging (GCM) messages, and use GCM messages to + efficiently sync with the server.</dd> </dl> diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index 77a6837..37b69d4 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -203,14 +203,14 @@ <span class="en">Syncing to the Cloud</span> </a></div> <ul> - <li><a href="<?cs var:toroot ?>training/cloudsync/aesync.html"> - <span class="en">Syncing with App Engine</span> - </a> - </li> <li><a href="<?cs var:toroot ?>training/cloudsync/backupapi.html"> <span class="en">Using the Backup API</span> </a> </li> + <li><a href="<?cs var:toroot ?>training/cloudsync/gcm.html"> + <span class="en">Making the Most of Google Cloud Messaging</span> + </a> + </li> </ul> </li> |