[筆記] Google Cloud Messaging

Google Cloud Messaging的前身是Cloud to Device Messaging Framework (簡稱 C2DM ),目的是將資料傳送到手機端進行處理,常見的應用如Chrome to Phone或其他會發通知的app都靠此功能完成。

教學連結 GCM: Getting Started

前置工作

建立Google API Project

  • 開啟Google Cloud Messaging for Android

開啟Google Cloud Messaging服務

  • 取得API Key

當Server端要發送資料時會需要

  • 取得Sender ID

Sender ID是Android裝置跟服務註冊的時候指定發送者的ID,我只知道從網址列這種看法…

取得Sender ID,內容是一串數字。

建立Android VM

這段不是一定要做,主要是如果當初本來就是選了Google APIs的AVD就沒必要,要是原本使用的是Android AVD,因為模擬器中沒有Google API相關的libary所以會發生錯誤。

選取Google APIs的AVD

選取Google APIs的AVD

安裝相關libary

如連結中教學所示,到SDK Manager選Extras勾選下載 Google Cloud Messaging for Android Library

裝置端程式

  • 修改AndroidManifest.xml

限制最低支援SDK版本(Android 2.2)


  • 宣告必要權限

取得帳號其實並非必要權限,但伺服器端必須透過這些資訊管理要發送的對象






  • 宣告broadcast receiver與intent service








  • 撰寫自己的GCMIntentService
繼承com.google.android.gcm.GCMBaseIntentService,需注意這邊要宣告無參數的建構子
public class GCMIntentService extends GCMBaseIntentService {

 private static final String senderId = your_sender_id;

 public GCMIntentService() {
 super(senderId);
 Log.d("GCMIntentService", senderId);
 }
需要override各種方法,主要重點在onMessage這個接收到訊息後的相關處理,這邊是使用常見的通知列通知。
@Override
protected void onMessage(Context context, Intent intent) {
 String ns = Context.NOTIFICATION_SERVICE;
 NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
//宣告圖示
 int icon = R.drawable.your_icon;
//通知標題
 CharSequence tickerText = "your ticker text";
 long when = System.currentTimeMillis();
 CharSequence contentTitle = "your content title";
//取得通知內容
 CharSequence contentText = intent.getStringExtra("message");
//點選通知後跳出的activity
 Intent notificationIntent = new Intent(this, YourActivity.class);
 PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
 Notification notification = new Notification(icon, tickerText, when);
//點選後自動移除該通知
 notification.flags = Notification.FLAG_AUTO_CANCEL;
 notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
 mNotificationManager.notify(notice_id, notification);
 }

在寫code的同時會發現IDE顯示裡面部份API都已經是deprecated,會建議使用NotificationBuilder,但會有無法向下相容到2.2的問題。

  • 在主activity註冊訊息服務

程式啟動後利用Sender ID取得一組Registration ID,而Server端會利用這個Registration ID來找到要發送的目的裝置,但由於它只是個代碼,所以需搭配其他識別(如使用者帳號)進行處理。

 public static final String GSF_PACKAGE = "com.google.android.gsf";
 public static final String REQUEST_REGISTRATION_INTENT = "com.google.android.c2dm.intent.REGISTER";

private void init(){
 Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
 registrationIntent.setPackage(GSF_PACKAGE);
 GCMRegistrar.checkDevice(this);
 GCMRegistrar.checkManifest(this);
 final String regId = GCMRegistrar.getRegistrationId(this);
 Log.d("regId",regId);
 if (regId.equals("")) {
 GCMRegistrar.register(this, GCMIntentService.senderId());
 } else {
 Log.v("YourActivity", "Already registered");
 }
 }

伺服器端程式

  • 建立Server接收Registration ID機制

由於Server在發動訊息時會透過此ID發送,所以需要一個機制來接收裝置端送來的Registration ID,自行實做即可,這邊我用cp大法就沒有範例了…

  • 撰寫發送訊息程式
單機版也可以測試,程式需加入相依的jar檔,必備有的有gcm-server.jar(位於android-sdk\extras\google\gcm\gcm-server\dist\)以及JSON.Simple
//建立sender
 Sender sender = new Sender("Your API key");
//加入欲發送的訊息,注意這裡message這個key是自訂的,與裝置端配合即可
 Message message = new Message.Builder().addData("message", "Your Message").build();
//發送時提供Registration ID來決定欲傳送裝置
 Result result = sender.send(message, "Registration ID", 5);
 if (result.getMessageId() != null) {
 String canonicalRegId = result.getCanonicalRegistrationId();
 if (canonicalRegId != null) {
 // same device has more than on registration ID: update database
 }
 } else {
 String error = result.getErrorCodeName();
 if (error.equals(Constants.ERROR_NOT_REGISTERED)) {
 // application has been removed from device - unregister database
 }
 }
完成
執行server端程式,裝置端收到通知就代表成功了。
Hello,GCM! :)

大功告成

 

※注:如果看到your.package_name之類的,那就是要改成你自己在用的變數,比如project packagename是 hello.world, 那 your.package_name就要改成 hello.world以此類推….

 

71 thoughts on “[筆記] Google Cloud Messaging

  1. 你好,

    可以跟你請教,最後你SEVER端是用什麼程式跑嗎。

    還有可以跟你要Server接收Registration ID機制的CODE嗎?

    感謝你的幫忙,在這方面找不太到相關訊息

    我的信箱;i002149@hotmail.com

    • 哈囉,

      我Server端很簡陋,就是一個Java class,因為沒幾行就直接跑在一個main裡面了。
      (內容就跟本篇上面寫得內容一模一樣)

      Registration ID傳遞的部份你可以用RESTful的方式,在伺服器端實做接收方式,然後裝置端透過HTTP POST的方式將資訊傳遞過去就可以了。

      最陽春的就是用Servlet來達成,覆寫doPost之後,在將ID用參數的方式接收進來,Android裝置端的部份有HttpClient相關API可以call,網路上有很多相關的教學,應該不會太困難才是。

  2. 請問一下server端我程式執行 就是會有錯誤 請問一下
    我看google api
    server 端不是這樣就可以送出去了嗎?他的devices我有點不太懂

    Sender sender = new Sender(myApiKey);
    Message message = new Message.Builder().build();
    MulticastResult result = sender.send(message, devices, 5);

    • 你這邊的devices就是文中要填Registration id的地方,意思就是要告訴服務訊息是要送往那一台裝置,
      而這個id在向GCM服務註冊的時候就會得到了,可以參考「在主activity註冊訊息服務」跟「建立Server接收Registration ID機制」。

  3. 不好意思 在請問一下 你的程式我模擬都改好了編譯都沒問題 我開server 接收裝置也都沒反應 我想說是不是server問題 找了c# code來送 好像也沒回應 當下毫無頭緒…可以請教一下嗎?不知道是哪端出問題…

  4. 你好 server那段錯誤解決了 但是接收端還是收不到 我有用google測試程式測過 我的send id
    跟key 是沒問題的 程式也排除錯誤了 不好意思方便留e-mail嗎?可否寄給你幫我看一下嗎?

      • 我其實沒看懂 因為裝置端拿到Reg id之後,要提供給server端,
        這樣server才能透過這個Reg id跟GCM伺服器指定要傳送的對象。

        如果中間整個流程跟參數傳遞都對了 應該不容易出錯
        真的還有其他問題就要另外看了
        Good luck.

  5. ■撰寫發送訊息程式
    請教關於這段程式碼,是只要有這段程式碼就可以把推播訊息送給手機裝置了嗎??
    我目前可以在Clinet端取得手機裝置ID,但是我想要傳送推播訊息給手機,這段不知道如何使用。請問以下這兩行程式碼就是執行將推播發至手機端嗎??key自訂的是什麼意思??

    // 加入欲發送的訊息,注意這裡message這個key是自訂的,與裝置端配合即可
    Message message = new Message.Builder().addData(“message","Your Message").build();

    / 發送時提供Registration ID來決定欲傳送裝置
    Result result = sender.send(message, rid, 5);

  6. 請教一下,我收到訊息後手機上也可以顯示收到的內容
    我在onMessage,把內容搬到下一個Activity,第一次可以顯示成功
    但是第2次後就一直卡上一次的訊息
    我有測過IntentService的生命週期,他搬完後就會關掉
    我也看過搬過去的值是新的,但是Activity內的訊息卻一直是上次的內容
    可以請教一下這是什麼問題呢?

  7. Registration ID 收不到~~依照作者提供的範例,收不到Registration Id
    我是在模擬器環境底下操作,而send_id是用自己申請的
    請問作者怎麼解決的,謝謝

  8. 你好,請問您一下,您程式碼的第十二行
    GCMRegistrar.register(this, GCMIntentService.senderId());
    這個senderId() method 是內建的method嗎? 我找不到說?!

  9. 請問我現在如果server是Json格式,那我onMessage()要怎麼接收~我現在手機傳到GCM有辦法接收~但是從server傳到GCM,我手機就接收不到~會有啥辦法解決或有可能問題出在哪?可以幫我分析一下嗎?

  10. 作者你好:

    可以跟你請教,最後你SEVER端是的程式碼跟如何把RegID傳到Server的程式碼嗎?

    因為還是學生初學Android,感謝你寫這篇教學文!受益良多

    感謝你的幫忙與上次的回覆

    我的信箱;komlllcf@hotmail.com

  11. 作者你好,請問GCM 是否在 2013 IO大會上宣布改版
    我依照你的做法在取得的RIG ID 已經取不到了
    他好像在 GCMRegistrar.checkDevice(this); 這行就會出錯了寫androidRuntime 之前 都不會有這問題,請問我該怎麼辦 可以教我媽 ? komlllcf@hotmail.com 謝謝

      • 作者你好,小弟不才真的看了一上午的官方文件,真的是有點看不明白,由於googleGCM更新太新霎那間都搜尋不到相關教學,真的不知道如何下手,也看他的SDK官方範例越看越糊.可以請作者寫篇教學文.或者麻煩您改寫你之前code 寄給我(komlllcf@hotmail.com) 學習嗎?! 小弟 真的感激萬分!拜託了>< 謝謝你

      • 剛剛發現舊的程式還是可以用,只是官方不建議你這麼做而已。
        我也實際跑過一次,是沒問題的,所以我想你的問題應該是在其他地方。

        另外這篇文章與其說是教學,不如說是當初心血來潮的筆記,
        用來紀錄我實際上在學習GCM的過程。
        (我暫時也沒打算與心力更新這個部份)
        而且內容與官方文件幾乎無異,只把一些新手可能會漏掉的部份加進去而已。
        程式碼的部份官方其實已經很詳盡了,其他設定好照co並不太會有問題。

  12. 超級感謝你抽空回答,我又再打一遍eclipse都重灌,還是會出現錯誤(在LOG檔案裡Tag=androidRuntime)目前很努力試試看. 另外我可以私心跟你要你的程式碼檔案client跟sever部分嗎? 我只是想自己學習跟參考 絕對不會外流與張貼 ,可以嗎?! 小弟不才真的很謝謝你的幫忙與回答.

  13. 不好意思 我想請教您~ 因為我們有依照你的方法實作…
    GCM可以使用~可以傳送~
    但是如果是輸入中文就會程式整個結束執行,
    不知道這是不是我們編碼方面出問題…還是甚麼狀況~
    不曉得能不能請你幫忙…>"< 謝謝!!!

    • Hi,

      雖然我想不出可能的狀況,但我建議你可以先從錯誤訊息下手。
      看上下文我只能猜測是發送的程式出錯(還是接收的出錯?)

      無論哪邊都一定會跳例外訊息,針對發生的例外訊息進行除錯是最好的方式。

  14. 不好意思,我是新手, 當我傳送訊息至手機端時手機端的app 會顯示app 已停止及需確定, 我在網上找了很久.,請問一下是在那裡出錯.{“multicast_id":xxxxxxxx,"success":1,"failure":0,"canonical_ids":1,"results":[{“registration_id":"xxxxxxx","message_id":"0:xxxxxxx"}]}

    謝謝幫忙

    • Hi,

      你的訊息並沒有錯誤訊息,其實你應該看你手機或者模擬器在logcat所拋出的exception訊息進行判斷,
      但可以確認的是你的gcm已經成功了,真的有問題是你接收到訊息後的處理。

      p.s. 你原文中有許多識別id,為避免不必要的問題(如被冒用),建議修改過後再貼上,我已經先幫你改過了。

  15. 謝謝回覆, 我有看過LOGCAT :
    java.lang.RuntimeException: Unable to instantiate receiver com.google.android.gcm.GcmBroadcastReceiver: java.lang.ClassNotFoundException: Didn’t find class “com.google.android.gcm.GcmBroadcastReceiver"

    我已經修改過manifest:內 :
    android:name="com.google.android.gcm.GcmBroadcastReceiver"

    請問還有那地方出錯??

    謝謝!

    • Hi,
      依據錯誤訊息的確是找不到指定的Class,所以從錯誤判斷就只能這樣,
      或許你可以依據你的stack trace找出拋出例外地方,看是你自己的程式碼或者是其他lib丟出來的,
      進而判斷錯誤發生的原因。

  16. Hi,
    謝謝你之前提出的資料,. 已經知道是google API 內Google Cloud Messaging出錯,現在沒有錯誤出現, 在手機上面可以收到通知ICON,但按下沒有任何訊息出現,還在找尋中.

    • Hi,
      一般如果是要與網頁服務整合,就Java的角度是不需要轉成網頁,而是透過網頁呼叫你上面提到的程式碼部分(如寫在Servlet),php的話我沒用過,但稍微看一下有人會透過GCM本身的RESTful API,使用curl的方式進行傳送,相關範例可以透過google “GCM PHP"等關鍵字找到,可以參考看看。

  17. Hi, 不好意思, 我講不清楚, 我設定mysql Server 後, 但手機發送regid到Server 不接, 因為我對mysql 不太了解, 我用PHPmyadmin 查過database 沒有資料.請問用怎麼方法查看那裡出錯.

    • 我認為你可以分兩段來看,一個是server是否有接收到regId,再來是regId是否有成功被存入資料庫,你可以透過輸出的方式,查看這兩個階段是不是正常,比較方便釐清問題。

      以Java的做法大概類似這樣,
      //收到Log後
      Logger.getLogger(this.getClass()).info(“regId : “+regId);

      //存入資料庫
      public void persist(Entity entity){
      try{
      Logger.getLogger(this.getClass()).info(“Persisting regId : “+entity.getRegId());
      //寫入資料庫
      }catch(Exception ex){
      //寫入失敗 印出錯誤
      Logger.getLogger(this.getClass()).error(“Error persisting regId : “+regId. ex);
      }finally{
      //關閉連線
      }
      }

      另外如果你不熟mysql 應該先確認是否你可以正確的把資料寫入
      如果有開啟transaction,那就要transaction begin commit寫好

  18. Hi, 不好意思又麻煩你,當程式運行中發訊息到手機是沒有問題,當程式結束後再發訊息click 入app內沒有顯示訊息而即時再發訊息可看見現在的訊息,但第一次發的訊息沒有反映. 應該是進入app 時它不會在BroadcastReceiver的onReceive內取intent 訊息. 我在網上找了很久,請問怎樣處理. 謝謝你的幫忙.

    • Hi, 你的意思是你程式沒有在運作的時候無法正確將訊息丟給你的intent嗎?
      這個部份我的理解是應該要檢查override GCMBaseIntentService 的那個服務的onMessage方法。
      至於Broadcastreciever的部分不知道為什麼連這篇文章的xml都被刪除了,我也無法找到當初的文件了。
      總之,我只能大概提個方向,不太確定狀況是怎樣…

      其實我比較建議你不要參考這篇文章
      因為這個已經是幾年前的做法了 現在Google把訊息服務改為Google Cloud Messaging
      http://developer.android.com/reference/com/google/android/gms/gcm/GoogleCloudMessaging.html
      或許這邊對現在版本的android的運作才是比較相容的
      而且我也必須老實講 這文章真的太久了 我自己也都快忘光光了..

      Good luck.

發表留言