Google Cloud Messaging(GCM Google云推送)

来源:互联网 发布:seo网络培训seo8 编辑:程序博客网 时间:2024/05/20 18:44

http://blog.csdn.net/change_from_now/article/details/39694851





8月不写博,9月徒伤悲,10月徒伤悲... ....。8月居然没写博,罪过罪过啊~~ 在9月的最后一天还是赶紧写几篇博文吧,要不真的要一直徒伤悲了。


写在前面:

GCM---Google云推送的简称,就是我们常见的消息推送(本来是个很好的功能,却被国内软件搞臭了。每天看到手机上满满的推送消息,简直不能忍)。其实在国内有几个推送的SDK,用起来也可以,但是本着多学习Google知识,还是选用GCM吧。同先前所说,这些内容都是可以在Google Android的开发官网上看到Android Developer,我只算个搬运工,然后会说一些需要注意的地方。这个比前面所说的内支付要简单的多,一篇可以搞完。


开篇:

1.添加Google Play Services工程。使用GCM需要先把Google Play Services这个静态工程引入到自己的工程中。具体从哪里获得这个静态工程以及如何引入,可以参考我写的内支付的上篇,也可以去官网看Setting Up Google Play Services。

2.去Google Developers Console创建工程。 要启用GCM,需要到Google Developers Console创建你的工程,创建完工程后,会获得一个Project Number(客户端使用)和 API Key(服务器使用)。创建工程步骤如下(趁现在vpn可以用,就演示下^-^)。

(1)打开Console网站后显示如下,点击Create Project工程创建你的工程。在弹出的框中写上你的工程名字。


点击create后,在界面右下方会显示工程正在创建中,创建工程也就花费几秒钟时间,稍等一会儿,创建成功后出现如下界面

点开API & auth,在下拉列表中选择 API,这时会在右侧出现很多列表,下拉到Google Cloud Messaging for Android 这一栏,在最右边有个开关按钮,点击按钮,就可以打开此项功能了。


再点击左边API下面的 Credentials,在弹出的界面中Public API access的下方选择Create new Key,在弹出框中根据需要选择要创建key的类型,这里使用的是Android key.



点击Android key后会弹出一个界面,根据提示要输入你Key Store的SHA1 fingerprints,程序包名如何获取这个SHA,界面上已经给出了命令,很简单。

keytool -list -v -keystore mystore.keystore

在输入了SHA1和包名后点击Create.SHA1和包名用分号隔开,输入框上有example.



在最后的显示界面上找到那个API key.Project Number在工程预览的上面就可以看到,如下图视,有了这两个值后就可以开工写代码了。


3.开工写代码 客户端:

在写代码前先提供个官方例子代码,有客户端,服务器端以及一个Google自己的servlet AppEngine,类似Tomcat,做服务器应该都熟悉。下载地址在我的资源里GCM Demo

android Manifest的设置以及代码,我就照搬官网教程了,有条件的可以去官网看源码。

(1).AndroidManifest 设置

[html] view plaincopyprint?
  1. <manifest package="com.example.gcm" ...>  
  2.   
  3.     <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17"/>  
  4.     <uses-permission android:name="android.permission.INTERNET" />  
  5.     <uses-permission android:name="android.permission.GET_ACCOUNTS" />  
  6.     <uses-permission android:name="android.permission.WAKE_LOCK" />  
  7.     <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />  
  8.   
  9.     <permission android:name="com.example.gcm.permission.C2D_MESSAGE"  
  10.         android:protectionLevel="signature" />  
  11.     <uses-permission android:name="com.example.gcm.permission.C2D_MESSAGE" />  
  12.   
  13.     <application ...>  
  14.         <receiver  
  15.             android:name=".GcmBroadcastReceiver"  
  16.             android:permission="com.google.android.c2dm.permission.SEND" >  
  17.             <intent-filter>  
  18.                 <action android:name="com.google.android.c2dm.intent.RECEIVE" />  
  19.                 <category android:name="com.example.gcm" />  
  20.             </intent-filter>  
  21.         </receiver>  
  22.         <service android:name=".GcmIntentService" />  
  23.     </application>  
  24.   
  25. </manifest>  


把上面xml中的com.example.gcm改成自己工程的包名就可以直接使用了。

练习英文翻译,下面是官网教程的中文翻译。

  •  com.google.android.c2dm.permission.RECEIVE Android程序可以注册和接收消息的权限
  • android.permission.INTERNET 这个最常用了,联网的权限。官网原话说的是可以让程序向第三方服务器发送registration ID的权限。这个功能下面细表。
  • The android.permission.GET_ACCOUNTS 获取设备Google账户权限。在SDK4.0.4一下的设备上使用GCM,GCM需要获取Google账户。
  • The android.permission.WAKE_LOCK 当设备收到消息后,可以唤醒设备。这个是可选的,如果你想实现消息达到后唤醒黑屏待机的设备就可以把这个权限加上。
  • An applicationPackage + ".permission.C2D_MESSAGE" 这个权限中包含你的包名,这样就可以防止其他应用程序注册或者也收到服务器推送的消息。包名必须和程序包名严格一致,否则程序无法收到服务器推送的消息。
  • com.google.android.c2dm.intent.RECEIVE 为Receiver所开的权限,工程里需要一个Receiver接收消息,这个Receiver会添加一个applicationPackagecategory。这个Receiver还需要添加com.google.android.c2dm.SEND permission,这样就只会让GCM Framework可给它发消息。如果你的程序需要一个IntentService (不是必须的,但是比较常用),这个Receiver就应该是一个WakefulBroadcastReceiver.的实例。WakefulBroadcastReceiverkes可以用来为你的程序创建和管理partial wake lock,这部分的内容后面会讲到。
  • 一个用来在WakefulBroadcastReceiver中处理GCM message的Service(一般是一个IntentService),可以在处理过程中让设备保持唤醒状态。IntentService是可选的,你也可以选择用BroadcastReceiver来处理消息,不过实际使用中,大多数程序还是用的IntentService。
  • If the GCM feature is critical to the Android application's function, be sure to set android:minSdkVersion="8" or higher in the manifest. This ensures that the Android application cannot be installed in an environment in which it could not run properly.
  • 如果GCM的某些特性在你的程序功能使用上很严格,就要考虑在manifest里android:minSdkVersion="8" 或者设置的更高。这样就可以保证你的程序不会安装到那些会导致程序运行异常的环境中。
  • (翻译果然不行啊,继续努力!)

(2)程序代码 继续照搬官网教程

客户端这边流程就是向GCM server请求register Id ---->本地保存Id ------>向自己的推送服务器发送register Id ----->等待接收message

@1检测当前设备是否支持Google Play.同内支付,要在设备上使用GCM也是需要设备上装有 Google Play。这里重复一下检测Google Play的源码,很简单。

[html] view plaincopyprint?
  1. private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;  
  2. ...  
  3. @Override  
  4. public void onCreate(Bundle savedInstanceState) {  
  5.     super.onCreate(savedInstanceState);  
  6.   
  7.     setContentView(R.layout.main);  
  8.     mDisplay = (TextView) findViewById(R.id.display);  
  9.   
  10.     context = getApplicationContext();  
  11.   
  12.     // Check device for Play Services APK.  
  13.     if (checkPlayServices()) {  
  14.         // If this check succeeds, proceed with normal processing.  
  15.         // Otherwise, prompt user to get valid Play Services APK.  
  16.         ...  
  17.     }  
  18. }  
  19.   
  20. // You need to do the Play Services APK check here too.  
  21. @Override  
  22. protected void onResume() {  
  23.     super.onResume();  
  24.     checkPlayServices();  
  25. }  
  26.   
  27. /**  
  28.  * Check the device to make sure it has the Google Play Services APK. If  
  29.  * it doesn't, display a dialog that allows users to download the APK from  
  30.  * the Google Play Store or enable it in the device's system settings.  
  31.  */  
  32. private boolean checkPlayServices() {  
  33.     int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);  
  34.     if (resultCode != ConnectionResult.SUCCESS) {  
  35.         if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {  
  36.             GooglePlayServicesUtil.getErrorDialog(resultCode, this,  
  37.                     PLAY_SERVICES_RESOLUTION_REQUEST).show();  
  38.         } else {  
  39.             Log.i(TAG, "This device is not supported.");  
  40.             finish();  
  41.         }  
  42.         return false;  
  43.     }  
  44.     return true;  
  45. }  

@2 注册GCM. 在安装当前程序的设备接收GCM message之前需要向GCM servers发送一个注册请求,成后会收到一个 registration ID,收到这个ID后需要保存在本地,这样就不需要每次在程序启动的时候去请求这个ID了,在程序更新版本的时候需要重新请求这个ID. 下面的Your-Sender-ID换成你上面获得的Project num

[html] view plaincopyprint?
  1. /**  
  2.  * Main UI for the demo app.  
  3.  */  
  4. public class DemoActivity extends Activity {  
  5.   
  6.     public static final String EXTRA_MESSAGE = "message";  
  7.     public static final String PROPERTY_REG_ID = "registration_id";  
  8.     private static final String PROPERTY_APP_VERSION = "appVersion";  
  9.     private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;  
  10.   
  11.     /**  
  12.      * Substitute you own sender ID here. This is the project number you got  
  13.      * from the API Console, as described in "Getting Started."  
  14.      */  
  15.     String SENDER_ID = "Your-Sender-ID";  
  16.   
  17.     /**  
  18.      * Tag used on log messages.  
  19.      */  
  20.     static final String TAG = "GCMDemo";  
  21.   
  22.     TextView mDisplay;  
  23.     GoogleCloudMessaging gcm;  
  24.     AtomicInteger msgId = new AtomicInteger();  
  25.     SharedPreferences prefs;  
  26.     Context context;  
  27.   
  28.     String regid;  
  29.   
  30.     @Override  
  31.     public void onCreate(Bundle savedInstanceState) {  
  32.         super.onCreate(savedInstanceState);  
  33.   
  34.         setContentView(R.layout.main);  
  35.         mDisplay = (TextView) findViewById(R.id.display);  
  36.   
  37.         context = getApplicationContext();  
  38.   
  39.         // Check device for Play Services APK. If check succeeds, proceed with  
  40.         //  GCM registration.  
  41.         if (checkPlayServices()) {  
  42.             gcm = GoogleCloudMessaging.getInstance(this);  
  43.             regid = getRegistrationId(context);  
  44.   
  45.             if (regid.isEmpty()) {  
  46.                 registerInBackground();  
  47.             }  
  48.         } else {  
  49.             Log.i(TAG, "No valid Google Play Services APK found.");  
  50.         }  
  51.     }  
  52. ...  
  53. }  


检测本地是否已经存储了此ID

[html] view plaincopyprint?
  1. /**  
  2.  * Gets the current registration ID for application on GCM service.  
  3.  * <p>  
  4.  * If result is empty, the app needs to register.  
  5.  *  
  6.  * @return registration ID, or empty string if there is no existing  
  7.  *         registration ID.  
  8.  */  
  9. private String getRegistrationId(Context context) {  
  10.     final SharedPreferences prefs = getGCMPreferences(context);  
  11.     String registrationId = prefs.getString(PROPERTY_REG_ID, "");  
  12.     if (registrationId.isEmpty()) {  
  13.         Log.i(TAG, "Registration not found.");  
  14.         return "";  
  15.     }  
  16.     // Check if app was updated; if so, it must clear the registration ID  
  17.     // since the existing regID is not guaranteed to work with the new  
  18.     // app version.  
  19.     int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);  
  20.     int currentVersion = getAppVersion(context);  
  21.     if (registeredVersion != currentVersion) {  
  22.         Log.i(TAG, "App version changed.");  
  23.         return "";  
  24.     }  
  25.     return registrationId;  
  26. }  
  27. ...  
  28. /**  
  29.  * @return Application's {@code SharedPreferences}.  
  30.  */  
  31. private SharedPreferences getGCMPreferences(Context context) {  
  32.     // This sample app persists the registration ID in shared preferences, but  
  33.     // how you store the regID in your app is up to you.  
  34.     return getSharedPreferences(DemoActivity.class.getSimpleName(),  
  35.             Context.MODE_PRIVATE);  
  36. }  

检测程序新版本

[html] view plaincopyprint?
  1. /**  
  2.  * @return Application's version code from the {@code PackageManager}.  
  3.  */  
  4. private static int getAppVersion(Context context) {  
  5.     try {  
  6.         PackageInfo packageInfo = context.getPackageManager()  
  7.                 .getPackageInfo(context.getPackageName(), 0);  
  8.         return packageInfo.versionCode;  
  9.     } catch (NameNotFoundException e) {  
  10.         // should never happen  
  11.         throw new RuntimeException("Could not get package name: " + e);  
  12.     }  
  13. }  


向GCM发送请求。在这里使用到了AsyncTask,代码也比较简单

[html] view plaincopyprint?
  1. /**  
  2.  * Registers the application with GCM servers asynchronously.  
  3.  * <p>  
  4.  * Stores the registration ID and app versionCode in the application's  
  5.  * shared preferences.  
  6.  */  
  7. private void registerInBackground() {  
  8.     new AsyncTask() {  
  9.         @Override  
  10.         protected String doInBackground(Void... params) {  
  11.             String msg = "";  
  12.             try {  
  13.                 if (gcm == null) {  
  14.                     gcm = GoogleCloudMessaging.getInstance(context);  
  15.                 }  
  16.                 regid = gcm.register(SENDER_ID);  
  17.                 msg = "Device registered, registration ID=" + regid;  
  18.   
  19.                 // You should send the registration ID to your server over HTTP,  
  20.                 // so it can use GCM/HTTP or CCS to send messages to your app.  
  21.                 // The request to your server should be authenticated if your app  
  22.                 // is using accounts.  
  23.                 sendRegistrationIdToBackend();  
  24.   
  25.                 // For this demo: we don't need to send it because the device  
  26.                 // will send upstream messages to a server that echo back the  
  27.                 // message using the 'from' address in the message.  
  28.   
  29.                 // Persist the regID - no need to register again.  
  30.                 storeRegistrationId(context, regid);  
  31.             } catch (IOException ex) {  
  32.                 msg = "Error :" + ex.getMessage();  
  33.                 // If there is an error, don't just keep trying to register.  
  34.                 // Require the user to click a button again, or perform  
  35.                 // exponential back-off.  
  36.             }  
  37.             return msg;  
  38.         }  
  39.   
  40.         @Override  
  41.         protected void onPostExecute(String msg) {  
  42.             mDisplay.append(msg + "\n");  
  43.         }  
  44.     }.execute(null, null, null);  
  45.     ...  
  46. }  

只是在我使用时

<span class="kwd" style="color: rgb(0, 0, 136);"><span style="background-color: rgb(247, 247, 247);">protected</span></span><span class="pln" style="color: rgb(0, 0, 0);"><span style="background-color: rgb(247, 247, 247);"> </span></span><span class="typ" style="color: rgb(102, 0, 102);"><span style="background-color: rgb(247, 247, 247);">String</span></span><span class="pln" style="color: rgb(0, 0, 0);"><span style="background-color: rgb(247, 247, 247);"> doInBackground</span></span><span class="pun" style="color: rgb(102, 102, 0);"><span style="background-color: rgb(247, 247, 247);">(</span></span><span class="typ" style="color: rgb(102, 0, 102);"><span style="background-color: rgb(247, 247, 247);">Void</span></span><span class="pun" style="color: rgb(102, 102, 0);"><span style="background-color: rgb(247, 247, 247);">...</span></span><span class="pln" style="color: rgb(0, 0, 0);"><span style="background-color: rgb(247, 247, 247);"> </span></span><span class="kwd" style="color: rgb(0, 0, 136);"><span style="background-color: rgb(247, 247, 247);">params</span></span><span class="pun" style="color: rgb(102, 102, 0);"><span style="background-color: rgb(247, 247, 247);">)</span></span>

这个方法时一直报错,最后没办法就改成了下面的这个版本

[html] view plaincopyprint?
  1. 先声明一个AsyncTask实例 AsyncTask<Void, Void, Void> mRegisterTask;  
[html] view plaincopyprint?
  1. /**  
  2.      * Registers the application with GCM servers asynchronously.  
  3.      * <p>  
  4.      * Stores the registration ID and app versionCode in the application's  
  5.      * shared preferences  
  6.      */  
  7.     private void registerInBackground()  
  8.     {  
  9.         Log.e(TAG, "Start Reg In Back");  
  10.           
  11.         mRegisterTask = new AsyncTask<Void, Void, Void>() {  
  12.   
  13.             protected Void doInBackground(Void... params) {  
  14.                 String msg = "";  
  15.   
  16.                 try {  
  17.                     if (gcm == null) {  
  18.                         gcm = GoogleCloudMessaging.getInstance(context);  
  19.                     }  
  20.                     regid = gcm.register(SENDER_ID);  
  21.   
  22.                     msg = "Device registered, registered ID = " + regid;  
  23.                     Log.v(TAG, msg);  
  24.   
  25.                     // You should send the registration ID to your server over  
  26.                     // HTTP.  
  27.                     // so it can use GCM/HTTP or CCS to send messages to your  
  28.                     // app.  
  29.                     // The request to your server should be authenticated if  
  30.                     // your app  
  31.                     // is using accounts.  
  32.                     ///sendRegistrationIdToOurServer(regid);  
  33.                     Map<String, String> mapId = new HashMap<String, String>();  
  34.                     mapId.put("regId", regid);  
  35.                       
  36.                     post(SERVER_URL, mapId);  
  37.   
  38.                     // For this demo:we don't need to send it because the device  
  39.                     // will send upstream messages to a sever that echo back the  
  40.                     // message using the 'from' address in the message.  
  41.   
  42.                     // Persist the regID - no need to register again  
  43.                     // storeRegistrationId(context, regid);  
  44.   
  45.                     setRegistrationId(context, regid);  
  46.                 } catch (IOException ex) {  
  47.                     msg = "Error:" + ex.getMessage();  
  48.                     // If there is an error.don't just keep trying to register.  
  49.                     // Require the user to click a button again,or perform  
  50.                     // exponential back-off  
  51.                 }  
  52.                   
  53.                 return null;  
  54.             }  
  55.   
  56.             protected void onPostExecute(Void msg) {  
  57.                 mDisplay.append(msg + "/n");  
  58.             }  
  59.   
  60.         };  
  61.           
  62.         mRegisterTask.execute(null, null, null);  
  63.     }  


获得register Id后存储到本地

[html] view plaincopyprint?
  1. /**  
  2.  * Stores the registration ID and app versionCode in the application's  
  3.  * {@code SharedPreferences}.  
  4.  *  
  5.  * @param context application's context.  
  6.  * @param regId registration ID  
  7.  */  
  8. private void storeRegistrationId(Context context, String regId) {  
  9.     final SharedPreferences prefs = getGCMPreferences(context);  
  10.     int appVersion = getAppVersion(context);  
  11.     Log.i(TAG, "Saving regId on app version " + appVersion);  
  12.     SharedPreferences.Editor editor = prefs.edit();  
  13.     editor.putString(PROPERTY_REG_ID, regId);  
  14.     editor.putInt(PROPERTY_APP_VERSION, appVersion);  
  15.     editor.commit();  
  16. }  


发送register ID到自己的推送服务器上。服务器可以把register ID和当前用户绑定在一起,这样就可以定向推送消息。上面提供的Demo里有这个实现,可以使用HttpURLConnection 把 registerID post到自己的推送服务器上。

这里简单贴一下代码,具体可以参考GCM Demo.

[html] view plaincopyprint?
  1. /**  
  2.  * Sends the registration ID to your server over HTTP, so it can use GCM/HTTP  
  3.  * or CCS to send messages to your app. Not needed for this demo since the  
  4.  * device sends upstream messages to a server that echoes back the message  
  5.  * using the 'from' address in the message.  
  6.  */  
  7. private void sendRegistrationIdToBackend() {  
  8.     // Your implementation here.  
[html] view plaincopyprint?
  1.         URL url;  
  2.         try {  
  3.             url = new URL("Your Push Server address");  
  4.         } catch (MalformedURLException e) {  
  5.             throw new IllegalArgumentException("invalid url: " + "<pre name="code" class="html" style="color: rgb(34, 34, 34); font-size: 14px; line-height: 19px;">Your Push Server address  

");
        }

[html] view plaincopyprint?
  1.  HttpURLConnection conn = null;  
  2.         try {  
  3.             conn = (HttpURLConnection) url.openConnection();  
  4.             conn.setDoOutput(true);  
  5.             conn.setUseCaches(false);  
  6.             conn.setFixedLengthStreamingMode(bytes.length);  
  7.             conn.setRequestMethod("POST");  
  8.             conn.setRequestProperty("Content-Type",  
  9.                     "application/x-www-form-urlencoded;charset=UTF-8");  
  10.             // post the request  
  11.             OutputStream out = conn.getOutputStream();  
  12.             out.write(bytes);  
  13.             out.close();  
  14.             // handle the response  
  15.             int status = conn.getResponseCode();  
  16.             if (status != 200) {  
  17.               throw new IOException("Post failed with error code " + status);  
  18.             }  
  19.         } finally {  
  20.             if (conn != null) {  
  21.                 conn.disconnect();  
  22.             }  
  23.         }  
  24. }  


@3 接收消息 Important----^-^

在上面manifest中说过

<span class="typ" style="color: rgb(102, 0, 102);"><span style="background-color: rgb(247, 247, 247);">GcmBroadcastReceiver</span></span><span class="pln" style="color: rgb(0, 0, 0);"><span style="background-color: rgb(247, 247, 247);"> 和 IntentService</span></span>

官网提供了最基本的实现,就是新建一个GcmBroadcastReceiver继承自

[html] view plaincopyprint?
  1. WakefulBroadcastReceiver   

新建一个GcmIntentService继承自

[html] view plaincopyprint?
  1. IntentService   

GcmBroadcastReceiver用来接收消息,收到消息后在Receiver中的GcmIntentService会处理接收到的消息。

[html] view plaincopyprint?
  1. public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {  
  2.     @Override  
  3.     public void onReceive(Context context, Intent intent) {  
  4.         // Explicitly specify that GcmIntentService will handle the intent.  
  5.         ComponentName comp = new ComponentName(context.getPackageName(),  
  6.                 GcmIntentService.class.getName());  
  7.         // Start the service, keeping the device awake while it is launching.  
  8.         startWakefulService(context, (intent.setComponent(comp)));  
  9.         setResultCode(Activity.RESULT_OK);  
  10.     }  
  11. }  


[html] view plaincopyprint?
  1. public class GcmIntentService extends IntentService {  
  2.     public static final int NOTIFICATION_ID = 1;  
  3.     private NotificationManager mNotificationManager;  
  4.     NotificationCompat.Builder builder;  
  5.   
  6.     public GcmIntentService() {  
  7.         super("GcmIntentService");  
  8.     }  
  9.   
  10.     @Override  
  11.     protected void onHandleIntent(Intent intent) {  
  12.         Bundle extras = intent.getExtras();  
  13.         GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);  
  14.         // The getMessageType() intent parameter must be the intent you received  
  15.         // in your BroadcastReceiver.  
  16.         String messageType = gcm.getMessageType(intent);  
  17.   
  18.         if (!extras.isEmpty()) {  // has effect of unparcelling Bundle  
  19.             /*  
  20.              * Filter messages based on message type. Since it is likely that GCM  
  21.              * will be extended in the future with new message types, just ignore  
  22.              * any message types you're not interested in, or that you don't  
  23.              * recognize.  
  24.              */  
  25.             if (GoogleCloudMessaging.  
  26.                     MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {  
  27.                 sendNotification("Send error: " + extras.toString());  
  28.             } else if (GoogleCloudMessaging.  
  29.                     MESSAGE_TYPE_DELETED.equals(messageType)) {  
  30.                 sendNotification("Deleted messages on server: " +  
  31.                         extras.toString());  
  32.             // If it's a regular GCM message, do some work.  
  33.             } else if (GoogleCloudMessaging.  
  34.                     MESSAGE_TYPE_MESSAGE.equals(messageType)) {  
  35.                 // This loop represents the service doing some work.  
  36.                 for (int i=0; i<5; i++) {  
  37.                     Log.i(TAG, "Working... " + (i+1)  
  38.                             + "/5 @ " + SystemClock.elapsedRealtime());  
  39.                     try {  
  40.                         Thread.sleep(5000);  
  41.                     } catch (InterruptedException e) {  
  42.                     }  
  43.                 }  
  44.                 Log.i(TAG, "Completed work @ " + SystemClock.elapsedRealtime());  
  45.                 // Post notification of received message.  
  46.                 sendNotification("Received: " + extras.toString());  
  47.                 Log.i(TAG, "Received: " + extras.toString());  
  48.             }  
  49.         }  
  50.         // Release the wake lock provided by the WakefulBroadcastReceiver.  
  51.         GcmBroadcastReceiver.completeWakefulIntent(intent);  
  52.     }  
  53.   
  54.     // Put the message into a notification and post it.  
  55.     // This is just one simple example of what you might choose to do with  
  56.     // a GCM message.  
  57.     private void sendNotification(String msg) {  
  58.         mNotificationManager = (NotificationManager)  
  59.                 this.getSystemService(Context.NOTIFICATION_SERVICE);  
  60.   
  61.         PendingIntent contentIntent = PendingIntent.getActivity(this, 0,  
  62.                 new Intent(this, DemoActivity.class), 0);  
  63.   
  64.         NotificationCompat.Builder mBuilder =  
  65.                 new NotificationCompat.Builder(this)  
  66.         .setSmallIcon(R.drawable.ic_stat_gcm)  
  67.         .setContentTitle("GCM Notification")  
  68.         .setStyle(new NotificationCompat.BigTextStyle()  
  69.         .bigText(msg))  
  70.         .setContentText(msg)  
[html] view plaincopyprint?
  1. <span style="white-space:pre">    </span>.setAutoCancel(true);  
  2.   
  3.         mBuilder.setContentIntent(contentIntent);  
  4.         mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());  
  5.     }  
  6. }  

代码也很简单,就是收到消息后使用NotificationManager这个组件来显示消息。我在源码后面加了一句setAutoCancel(true),这样就可以在用户点击了这个Notification后,Notification自动消失。

[html] view plaincopyprint?
  1. PendingIntent contentIntent = PendingIntent.getActivity(this, 0,  
  2.               new Intent(this, DemoActivity.class), 0);  

这段代码中的DemoActivity.class就是用户点击Notification后打开的Activity,可以根据需要改成你想让用户打开的Activity。

关注点

接收到的消息格式。这个消息格式可以和服务器约定好,官方推荐的是json格式。其中有几个值是必须要设置的,在下面说服务器时再讲。

4.服务器端:

服务器端这边可以直接参考上面提供的GCM Demo就行了。我就偷个懒吧,因为我搭建开发服务器的环境就搞了一下午,确实不懂啊!……

这里只说几个重点:

(1).把上面获取的API Key添加到服务器代码里,Demo中是在ApiKeyInitializer类中。

(2).消息参数和格式:

推送服务器发送的消息参数可以参考官网地址消息参数 

注意消息推送的过程是这样的,push server --> GCM server --> client。也就是说这个消息会先发送给GCM Server,GCM Server接收后再推送到客户端。

消息格式如下:

[html] view plaincopyprint?
  1. <message id="">  
  2.   <gcm xmlns="google:mobile:data">  
  3.   {  
  4.       "to":"REGISTRATION_ID",  // "to" replaces "registration_ids"  
  5.       "message_id":"m-1366082849205" // new required field  
  6.       "data":  
  7.       {  
  8.           "hello":"world",  
  9.       }  
  10.       "time_to_live":"600",  
  11.       "delay_while_idle": true/false,  
  12.       "delivery_receipt_requested": true/false  
  13.   }  
  14.   </gcm>  
  15. </message>  


消息解释:如上所示,是一个json格式的字符串。

to 后面的值是 registr ID。

message_id 后面的值 可以自己按需设定,无特别要求。

data里面就是消息内容,需要客户端来解析的。

time_to_live 消息在GCM server上保存的时间。当push server把消息push到GCM server后,GCM server再发送给客户端时,如果此时客户端离线,则这个消息会暂时保存到GCM server上,当设备在线后还可以再收到这个消息,如果过了这个保存时间,这个消息就会被GCM server清掉。

客户端和服务器端需要约定好data里面的消息格式,以便客户端收到消息后可以正确解析。

(2)Unregister GCM 取消注册GCM

良好的用户体验应该给用户选择的空间。这个推送功能是很好的,但是确被迫接收的,如果当手机上很多软件游戏都在推送时,很让人烦恼。所以要给用户一个是否选择接收推送消息的开关。

客户端中的 GoogleCloudMessaging 有一个取消注册方法。

[html] view plaincopyprint?
  1. unregister()  

这个是客户端通知GCM server取消注册。正常来说,也可以不使用这个方法,接收消息可以自己本地做处理,比如如果一个用户选择了不接收推送,客户端就可以把这个选项保存下来,不接收服务器发送来的消息就可以了。还有一种情况就是用户卸载了程序,这种情况push server是无法知道的,不过GCM server针对这个状况有个解决方法。

如果用户卸载了程序,当push server向GCM server发送消息,GCM server再向客户端发送消息,这时GCM server会收到程序不存在的反馈,然后GCM server会把这个register ID清除掉。当push server下次向GCM server发送消息时,因为消息中包含 register ID,GCM server就会返给push server一个unregister的消息,如过push server收到了这个消息就说明当前register Id的程序已经不存在了,这样push server在数据库里删了这个ID就可以了。


写在最后:

官网教程地址--->官网教程地址

GCM Demo资源地址 --->GCM Demo地址

0 0
原创粉丝点击