<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>润物无声 &#187; GCM</title>
	<atom:link href="http://blog.zhourunsheng.com/tag/gcm/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.zhourunsheng.com</link>
	<description>天空一朵雨做的云</description>
	<lastBuildDate>Sat, 08 May 2021 05:17:21 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.41</generator>
	<item>
		<title>Android云推送之GCM</title>
		<link>http://blog.zhourunsheng.com/2013/09/android%e4%ba%91%e6%8e%a8%e9%80%81%e4%b9%8bgcm/</link>
		<comments>http://blog.zhourunsheng.com/2013/09/android%e4%ba%91%e6%8e%a8%e9%80%81%e4%b9%8bgcm/#comments</comments>
		<pubDate>Sat, 28 Sep 2013 01:39:37 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[移动开发]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[GCM]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1860</guid>
		<description><![CDATA[<p>本文展示了怎样来利用谷歌的云推送系统Google Cloud Messaging (GCM)来推送消息到And [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/09/android%e4%ba%91%e6%8e%a8%e9%80%81%e4%b9%8bgcm/">Android云推送之GCM</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>本文展示了怎样来利用谷歌的云推送系统Google Cloud Messaging (GCM)来推送消息到Android手机，目前支持两大类消息，一类是“send-to-sync”，即发送一个触发指令，格式为一个自定义的字符串，例如 "syncTime"表示同步时间，那么Android客户端收到此消息的话，需要启动一个后台线程，同设定的Server进行交互，获取最新的时间；还有一类是“message with payload”，即发送一条消息，消息中可包含正文内容，比如message.addData("new_time", curTime)，则可以通过键值"new_time"获取到具体的时间。<span id="more-1860"></span></p>
<p>Pushing data has a number of benefits such as increased battery life and decreased server load. GCM is free to use and has no quotas, so it is a perfect candidate to handle the heavy lifting required for data updates.</p>
<p>There are two types of messages you can push with GCM: Send-to-sync and message with payload. A send-to-sync is basically a tickle, telling your app to sync with the server while a message with payload lets you send data in the push message (up to 4kb). Here’s a simple, full stack example that will help highlight how to get each of these types of push messages integrated with your app.</p>
<p>The app will take either a push message with a payload that updates the time that is saved on the device or a send to sync message that triggers the app to sync the time with the server. This was created using the “Generate App Engine Backend” feature of Android Studio. The Android developers blog has a <a href="http://android-developers.blogspot.com/2013/06/adding-backend-to-your-app-in-android.html">tutorial</a> on this. If you already have a backend setup, the GCM setup instructions for an existing server can be found <a href="http://developer.android.com/google/gcm/server.html">here</a>.</p>
<p>The source for this example is up on <a href="https://github.com/doubleencore/PushDontPoll" target="_blank">GitHub</a>.</p>
<h2>Create a new endpoint</h2>
<p>Wizard generated files <em>Message.java</em> and <em>MessageEndpoint.java</em> in PushDontPoll-AppEngine were deleted, along with the <em>messageendpoint</em> package in PushDontPoll-endpoints. In lieu of this, <em>DateEndpoint.java</em> was created in PushDontPoll-AppEngine. This class makes an endpoint at <em>/_ah/api/dateendpoint/v1/currentdate</em> that provides the current time in JSON:</p>
<pre>    @JsonAutoDetect
    public static class CurrentDate {

        @JsonProperty
        private long date = new Date().getTime();

        public long getDate() {
            return date;
        }
    }

    @ApiMethod(name = "currentDate")
    public CurrentDate getDate() {
        return new CurrentDate();
    }</pre>
<p>It also provides methods to send a message with payload and send to sync message via GCM. This was ripped out the deleted <em>MessageEndpoint.java</em> , so it only sends messages to a maximum of 10 devices. They are both built using GCM’s <a href="http://developer.android.com/reference/com/google/android/gcm/server/Message.Builder.html">Message.Builder()</a>.</p>
<p>The message with payload adds the time to the data parameter. Key/value pairs added here will be automatically added as extras in the the intent bundle when parsing the message.</p>
<pre>    String newTime = String.valueOf(new Date().getTime());
    Message message = new Message.Builder()
            .addData("new_time", newTime)
            .build();</pre>
<p>The send-to-sync message uses a collapse key so that only one message will be delivered if multiple tickles are queued up. You should not use more than four distinct keys since that is the maximum number that the GCM server can store. You can read more about this parameter <a href="http://developer.android.com/google/gcm/adv.html#msg-lifetime">here</a>.</p>
<pre>    Message message = new Message.Builder()
            .collapseKey("syncTime")
            .build();</pre>
<h2>Add send-to-sync and payload message buttons</h2>
<p>The <em>index.html</em> in the PushDontPoll-AppEngine project was modified to add buttons that allow us to send the two different types of push messages. Most of the wizard-generated code was removed for simplicity. The code to show the buttons is:</p>
<pre>  &lt;div&gt;
      &lt;button id="sendTickleButton"&gt;Send Tickle&lt;/button&gt;
  &lt;/div&gt;

  &lt;div&gt;
      &lt;button id="sendUpdateButton"&gt;Send Update&lt;/button&gt;
  &lt;/div&gt;</pre>
<p>The functions that they call are:</p>
<pre>  &lt;script type="text/javascript"&gt;
    $("#sendTickleButton").click(sendTickle);
    $("#successArea").hide();
  &lt;/script&gt;
  &lt;script type="text/javascript"&gt;
    $("#sendUpdateButton").click(sendUpdate);
    $("#successArea").hide();
  &lt;/script&gt;

  function sendTickle() {
    gapi.client.dateendpoint
    .sendTickle()
    .execute(handleMessageResponse);
  }

  function sendUpdate() {
    gapi.client.dateendpoint
    .sendUpdate()
    .execute(handleMessageResponse);
  }</pre>
<p>These call the <em>sendTickle()</em> and <em>sendUpdate()</em> methods in <em>DateEndpoint.java</em>. Make sure you re-generate the client libraries after changing the PushDontPoll-AppEngine code (Tools -&gt; Google Cloud Endpoints -&gt; Generate Client Libraries).</p>
<h2>Add code to parse GCM message</h2>
<p>The wizard-generated files <em>RegisterActivity.java</em> and <em>GCMIntentService.java</em> were moved to the main project so they could use the <em>Constants</em> class. The added application code to show the saved time was added to the <em>MainActivity</em>class in order to keep things separated and easy to understand. The <em>RegisterActivity</em> is accessible via an action item.</p>
<p>Parsing the GCM message is done in the <em>onMessage()</em> method in the <em>GCMIntentService</em> class. This class is already fleshed out for you thanks to the wizard.</p>
<h2>Parse send-to-sync</h2>
<p>The send-to-sync message will have a <em>collapse_key</em>, so we can request a sync if that matches the sync type we’re expecting:</p>
<pre>    if ("syncTime".equals(intent.getStringExtra("collapse_key"))) {
        Account[] accounts = AccountManager.get(this).getAccountsByType(Constants.ACCOUNT_TYPE);
        if (accounts.length &gt; 0) {
            Bundle bundle = new Bundle(2);
            bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
            bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
            ContentResolver.requestSync(accounts[0], Constants.AUTHORITY, bundle);
        }
        [snip]</pre>
<p>This uses a sync adapter with a stubbed authenticator and content provider – see <a href="http://developer.android.com/training/sync-adapters/index.html">here</a> for more info. This can easily be modified to send a network request to sync using your method of choice (Volley, Retrofit, etc) if a sync adapter is not desirable. All of the syncing code is in the <em>com.doubleencore.android.pushdontpoll.sync</em> package, along with the necessary xml files in <em>res/xml/</em> and in the <em>AndroidManifest.xml</em>.</p>
<p>In the <em>onPerformSync()</em> of the <em>SyncAdapter</em>, we can make a network request to the new endpoint that was created to get the current time:</p>
<pre>    Dateendpoint.Builder endpointBuilder = new Dateendpoint.Builder(
            AndroidHttp.newCompatibleTransport(),
            new JacksonFactory(),
            new HttpRequestInitializer() {
                @Override
                public void initialize(HttpRequest httpRequest) throws IOException {
                }
            });

    Dateendpoint endpoint = CloudEndpointUtils.updateBuilder(endpointBuilder).build();
    try {
        CurrentDate date = endpoint.currentDate().execute();
        long time = date.getDate();
        [snip]</pre>
<h2>Parse message with payload</h2>
<p>The message with payload will have <em>new_time</em> as a key in the intent extras. This key coincides with the one we added when building the message. You can read more about the data parameter <a href="http://developer.android.com/google/gcm/server.html#send-msg">here</a>.</p>
<pre>    if (intent.hasExtra("new_time")) {
        // Extract the data from the message
        long time = Long.parseLong(intent.getStringExtra("new_time"));
        [snip]</pre>
<p>In both cases, the time is then saved to shared preferences, and a local broadcast is sent to update the UI. Again, this can be done using your preferred method (event bus, observable cursor, etc).</p>
<pre>    SharedPreferences.Editor editor = getSharedPreferences(Constants.PREFS_NAME, 0).edit();
    editor.putLong(Constants.PREFS_KEY_TIME, time).apply();

    Intent updateIntent = new Intent(Constants.UPDATE_UI_INTENT_ACTION);
    LocalBroadcastManager.getInstance(this).sendBroadcast(updateIntent);</pre>
<h2>Running the server</h2>
<p>In order to use GCM, you must <a href="http://developer.android.com/google/gcm/gs.html">have an API key</a>. You can enter this when running the wizard or by replacing it in <em>DateEndpoint.java</em> in the PushDontPoll-AppEngine project. The server can either be deployed as an App Engine app or run locally using the dev server.</p>
<h2>Local instance</h2>
<p>To run it as a local instance, you need to change:</p>
<pre>    //PushDontPoll-endpoints Project - CloudEndpointUtils.java
    protected static final boolean LOCAL_ANDROID_RUN = true;</pre>
<p>Then you can just run the <em>appengine:devserver</em> maven goal, and it will be running on localhost. The server will now work with a Google API emulator.</p>
<p>If you want to access this local instance on a device (it must be on the same network as the local instance,) you’ll also need to change:</p>
<pre>    //PushDontPoll-endpoints Project - CloudEndpointUtils.java
    protected static final String LOCAL_APP_ENGINE_SERVER_URL_FOR_ANDROID = "http://[local_ip_address]:8080";</pre>
<p>…and download the App Engine SDK from <a href="https://developers.google.com/appengine/downloads">here</a>, and run:</p>
<pre>    appengine-java-sdk/bin/dev_appserver.sh --address 0.0.0.0 [project_directory]/PushDontPoll-Appegine/target/PushDontPoll-AppEngine-1.0</pre>
<h2>App Engine app</h2>
<p>If you didn’t follow along in the blog and are just trying to get the sample code running, you need to create a Google Cloud Platform project and obtain the Project Number and Project ID. You can do this by following the Preliminary Setup section of <a href="http://android-developers.blogspot.com/2013/06/adding-backend-to-your-app-in-android.html">this blog post</a>.</p>
<p>Set the <em>PROJECT_NUMBER</em> variable in <em>GCMIntentService.java</em> to your project number and the <em>&lt;application&gt;</em> tag in <em>appengine-web.xml</em> to your project ID, and regenerate the client libraries.</p>
<p>You may also want to change the packages (<em>com.doubleencore.android</em>) and domains (<em>doubleencore.com</em>) to your domain.</p>
<p>Deploy the backend server by running the <em>appengine:update</em> maven goal. You can then access your server at<em>http://&lt;project-id&gt;.appspot.com</em>.</p>
<h2>Running the Android app</h2>
<p>You can now deploy the PushDontPoll app to your device or emulator. Once deployed, click on the GCM Register action item to register your device with the server. Once the success message appears, press back and navigate to your server on your computer. Pressing the “Send Tickle” button will push a message that signals the device to sync with the server. The device will hit the date endpoint for the current time, and the time should refresh on the device. Pressing the “Send Update” button will push a message that has the new time in the payload data. This should instantly update the time shown on the device without needing to make an additional trip to the server.</p>
<p>Hopefully, this simple example app will assist you in getting a push framework up and running for your app. I highly recommend reading through the <a href="http://developer.android.com/google/gcm/index.html">GCM documentation</a> to get a thorough understanding, as there are many features not discussed in this article. You will need to handle things, such as request spikes with send-to-sync messages, more gracefully than this sample does as well. Good luck and happy pushing!</p>
<p>文章节选：<a href="http://www.doubleencore.com/2013/09/push-dont-poll-how-to-use-gcm-to-update-app-data/" target="_blank">http://www.doubleencore.com/2013/09/push-dont-poll-how-to-use-gcm-to-update-app-data/</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/09/android%e4%ba%91%e6%8e%a8%e9%80%81%e4%b9%8bgcm/">Android云推送之GCM</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/09/android%e4%ba%91%e6%8e%a8%e9%80%81%e4%b9%8bgcm/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
