Google Cloud Messaging的前身是Cloud to Device Messaging Framework (簡稱 C2DM ),目的是將資料傳送到手機端進行處理,常見的應用如Chrome to Phone或其他會發通知的app都靠此功能完成。
教學連結 GCM: Getting Started
前置工作
建立Google API Project
- 開啟Google Cloud Messaging for Android
- 取得API Key
- 取得Sender ID
Sender ID是Android裝置跟服務註冊的時候指定發送者的ID,我只知道從網址列這種看法…
建立Android VM
這段不是一定要做,主要是如果當初本來就是選了Google APIs的AVD就沒必要,要是原本使用的是Android AVD,因為模擬器中沒有Google API相關的libary所以會發生錯誤。
安裝相關libary
如連結中教學所示,到SDK Manager選Extras勾選下載 Google Cloud Messaging for Android Library
裝置端程式
- 修改AndroidManifest.xml
限制最低支援SDK版本(Android 2.2)
- 宣告必要權限
取得帳號其實並非必要權限,但伺服器端必須透過這些資訊管理要發送的對象
- 宣告broadcast receiver與intent service
- 撰寫自己的GCMIntentService
public class GCMIntentService extends GCMBaseIntentService { private static final String senderId = your_sender_id; public GCMIntentService() { super(senderId); Log.d("GCMIntentService", senderId); }
@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大法就沒有範例了…
- 撰寫發送訊息程式
//建立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 } }
※注:如果看到your.package_name之類的,那就是要改成你自己在用的變數,比如project packagename是 hello.world, 那 your.package_name就要改成 hello.world以此類推….
你好,
可以跟你請教,最後你SEVER端是用什麼程式跑嗎。
還有可以跟你要Server接收Registration ID機制的CODE嗎?
感謝你的幫忙,在這方面找不太到相關訊息
我的信箱;i002149@hotmail.com
哈囉,
我Server端很簡陋,就是一個Java class,因為沒幾行就直接跑在一個main裡面了。
(內容就跟本篇上面寫得內容一模一樣)
Registration ID傳遞的部份你可以用RESTful的方式,在伺服器端實做接收方式,然後裝置端透過HTTP POST的方式將資訊傳遞過去就可以了。
最陽春的就是用Servlet來達成,覆寫doPost之後,在將ID用參數的方式接收進來,Android裝置端的部份有HttpClient相關API可以call,網路上有很多相關的教學,應該不會太困難才是。
請問你server如果傳送中文訊息,android收到會是亂碼嗎? 謝謝。
我剛剛測試的結果是正常的。 🙂
那再請問你是如何在server處理中文訊息?
我是用servlet,謝謝。
http://caterpillar.onlyfun.net/Gossip/Encoding/Servlet.html 良葛格有一篇文章在講這件事情,參考看看吧。
有方式可以直接取得 Registration ID 嗎?
例如從手機上是否可以直接看到?
這就跟在手機上顯示一串文字的意思是一樣的啊。
只是Registration ID其實是很長一串,並不是給人閱讀用的。
請問我import com.google.android.gcm.GCMBaseIntentService; 不到?該怎麼做?
可以先檢查是否有在libs目錄內加入gcm.jar
請問API key 可以先使用開發電腦IP產生的key 用嗎?
這個API key是google產生給你的,等於是你使用API時候的辨識,跟自己用什麼電腦是無關的。
請問一下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機制」。
不好意思 在請問一下 你的程式我模擬都改好了編譯都沒問題 我開server 接收裝置也都沒反應 我想說是不是server問題 找了c# code來送 好像也沒回應 當下毫無頭緒…可以請教一下嗎?不知道是哪端出問題…
我看程式server Logcat 有錯誤警告Could not find class ‘com.google.android.gcm.server.Sender’, referenced from method com.example.bbb.BBB.onCreate
你必須在classpath加入gcm-server.jar
你好 server那段錯誤解決了 但是接收端還是收不到 我有用google測試程式測過 我的send id
跟key 是沒問題的 程式也排除錯誤了 不好意思方便留e-mail嗎?可否寄給你幫我看一下嗎?
補充一下..我接收端訊息是有拿到"Registration ID" 很長的一段Already registered, regId: XXXXXX-XXXX-XXX……
但是就是沒收到也沒有訊息..
我其實沒看懂 因為裝置端拿到Reg id之後,要提供給server端,
這樣server才能透過這個Reg id跟GCM伺服器指定要傳送的對象。
如果中間整個流程跟參數傳遞都對了 應該不容易出錯
真的還有其他問題就要另外看了
Good luck.
■撰寫發送訊息程式
請教關於這段程式碼,是只要有這段程式碼就可以把推播訊息送給手機裝置了嗎??
我目前可以在Clinet端取得手機裝置ID,但是我想要傳送推播訊息給手機,這段不知道如何使用。請問以下這兩行程式碼就是執行將推播發至手機端嗎??key自訂的是什麼意思??
// 加入欲發送的訊息,注意這裡message這個key是自訂的,與裝置端配合即可
Message message = new Message.Builder().addData(“message","Your Message").build();
/ 發送時提供Registration ID來決定欲傳送裝置
Result result = sender.send(message, rid, 5);
Result result = sender.send(message, rid, 5);
請問這行程式碼是傳送到GCMIntentService的onMessage()函示處理嗎???
這邊的key就是跟Map一樣的key value的概念
Server端的code是獨立的程式
至於onMessage 它不是傳送到onMessage 而是訊息送到的時候會觸發onMessage的event….
請問Reg id取到後要如何丟給server呢?
server端我想寫asp 或是php
請參考第一篇回覆,無論用何種技術,最簡單的就是用http method做,弄個表單給程式submit..
Android (Sender ID)-> Google(Register ID)->Android
請問要怎麼看確定有註冊成功? 可以把Register ID 用textView讀取出來看嗎
可以,你收到ID就代表註冊成功了…
請教一下,我收到訊息後手機上也可以顯示收到的內容
我在onMessage,把內容搬到下一個Activity,第一次可以顯示成功
但是第2次後就一直卡上一次的訊息
我有測過IntentService的生命週期,他搬完後就會關掉
我也看過搬過去的值是新的,但是Activity內的訊息卻一直是上次的內容
可以請教一下這是什麼問題呢?
其實這已經有點隔空抓藥了,光從這樣也很難判斷…
不過通常都有些小地方弄錯了 多檢查看吧
Registration ID 收不到~~依照作者提供的範例,收不到Registration Id
我是在模擬器環境底下操作,而send_id是用自己申請的
請問作者怎麼解決的,謝謝
我也不知道你錯在那…
你好,請問您一下,您程式碼的第十二行
GCMRegistrar.register(this, GCMIntentService.senderId());
這個senderId() method 是內建的method嗎? 我找不到說?!
http://developer.android.com/reference/com/google/android/gcm/GCMBaseIntentService.html
看api可能改了 我最近都沒碰 換getSenderIds看看吧
請問我現在如果server是Json格式,那我onMessage()要怎麼接收~我現在手機傳到GCM有辦法接收~但是從server傳到GCM,我手機就接收不到~會有啥辦法解決或有可能問題出在哪?可以幫我分析一下嗎?
收到JSON String就parse它就好了
至於後面 那真的只能駁杯才知道了…
作者你好: 請問 實作APPCLIENT的 SEVICE 部分
你的 notice_id 是什麼意思?! 找不道這個變數!:? 這個用意是要做什麼呢 ?
謝謝
這個notice_id其實就是給你的通知一個識別id,透過這個id進行識別。
如果你要取消這個通知,也可以透過這個id來進行取消。
詳情可以看官方的doc
http://developer.android.com/reference/android/app/NotificationManager.html#notify(int, android.app.Notification)
作者你好:
可以跟你請教,最後你SEVER端是的程式碼跟如何把RegID傳到Server的程式碼嗎?
因為還是學生初學Android,感謝你寫這篇教學文!受益良多
感謝你的幫忙與上次的回覆
我的信箱;komlllcf@hotmail.com
最常見的就是用http的方式送出 server端看要用網頁 用servlet 用web service接都好
你可以看官方的HttpClient api
http://developer.android.com/reference/org/apache/http/client/HttpClient.html
範例網路上應該很多才是
謝謝你的回答,另外想請教你 我收到的RegID 長得很像 API Key 是正確的嗎? 還是其實 是要收到像 SenderID 一樣一串數字呢?
正確的 應該都是hash過的值
作者你好,請問GCM 是否在 2013 IO大會上宣布改版
我依照你的做法在取得的RIG ID 已經取不到了
他好像在 GCMRegistrar.checkDevice(this); 這行就會出錯了寫androidRuntime 之前 都不會有這問題,請問我該怎麼辦 可以教我媽 ? komlllcf@hotmail.com 謝謝
我看了一下他的機制的確改了,他會希望你把Registration Id保留下來,
但是他會隨著時間或者改版過期。
所以上面的範例已經不能用了,你可以參考官方doc
http://developer.android.com/google/gcm/gs.html 中 getRegistrationId 跟 registerBackground這兩段code
(另外 setRegistrationId 可以幫助你儲存你的 Registration Id)
雖然我沒試過但官方doc照做應該就可以了 GL
作者你好,小弟不才真的看了一上午的官方文件,真的是有點看不明白,由於googleGCM更新太新霎那間都搜尋不到相關教學,真的不知道如何下手,也看他的SDK官方範例越看越糊.可以請作者寫篇教學文.或者麻煩您改寫你之前code 寄給我(komlllcf@hotmail.com) 學習嗎?! 小弟 真的感激萬分!拜託了>< 謝謝你
剛剛發現舊的程式還是可以用,只是官方不建議你這麼做而已。
我也實際跑過一次,是沒問題的,所以我想你的問題應該是在其他地方。
另外這篇文章與其說是教學,不如說是當初心血來潮的筆記,
用來紀錄我實際上在學習GCM的過程。
(我暫時也沒打算與心力更新這個部份)
而且內容與官方文件幾乎無異,只把一些新手可能會漏掉的部份加進去而已。
程式碼的部份官方其實已經很詳盡了,其他設定好照co並不太會有問題。
作者你好,經過一番螫疼,終於試出來不過適用舊版的方式! 不過我真得很感謝你的任何回答! 謝謝妳^^
恭喜你。
其實我的code真的全部都是網頁上那些了…
所以也沒有什麼給不給的問題,
通常會有錯誤都是哪裡有遺漏。
我可以提供我當初的code出來,對照之後就會發現跟我上面寫的沒什麼不同
http://db.tt/wrWyR5VI
超級感謝你抽空回答,我又再打一遍eclipse都重灌,還是會出現錯誤(在LOG檔案裡Tag=androidRuntime)目前很努力試試看. 另外我可以私心跟你要你的程式碼檔案client跟sever部分嗎? 我只是想自己學習跟參考 絕對不會外流與張貼 ,可以嗎?! 小弟不才真的很謝謝你的幫忙與回答.
如果你覺這樣不好小弟,可以跟你購買這兩個簡單的code嗎?!因為真的只是想學習用!^^
不好意思 我想請教您~ 因為我們有依照你的方法實作…
GCM可以使用~可以傳送~
但是如果是輸入中文就會程式整個結束執行,
不知道這是不是我們編碼方面出問題…還是甚麼狀況~
不曉得能不能請你幫忙…>"< 謝謝!!!
Hi,
雖然我想不出可能的狀況,但我建議你可以先從錯誤訊息下手。
看上下文我只能猜測是發送的程式出錯(還是接收的出錯?)
無論哪邊都一定會跳例外訊息,針對發生的例外訊息進行除錯是最好的方式。
你好
請問如果server 端用 ASP.NET C# 寫的話方式大概要怎麼做?
Hi,
其實我沒用.net開發過,但如果是接收id的部份,應該跟技術是無關的,
而是怎麼實做出的你的接收id的api,
比較麻煩的是屬於傳送訊息至手機端的部份,
我goolge了一下,發現是有相關的範例可尋的,
但這個topic其實我也很久沒有接觸了,不知道哪份才是適合你的,
就請你參考看看吧!
https://www.google.com.tw/?gfe_rd=cr&ei=J1QqVOPnHcSF8AWn-YC4Cg&gws_rd=ssl#q=gcm+.net
不好意思,我是新手, 當我傳送訊息至手機端時手機端的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,為避免不必要的問題(如被冒用),建議修改過後再貼上,我已經先幫你改過了。
謝謝回覆, 我有看過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"
請問還有那地方出錯??
謝謝!
ClassNotFoundException通常代表你的classpath沒設定好
以Adnroid的例子來說就是少了jar檔,記得檢查相關的lib有沒有放進去。
謝謝回覆, 在libs 上已有gcm.jar & android-support-v4.jar, & google-play-services.jar, 但還是不能.
Hi,
依據錯誤訊息的確是找不到指定的Class,所以從錯誤判斷就只能這樣,
或許你可以依據你的stack trace找出拋出例外地方,看是你自己的程式碼或者是其他lib丟出來的,
進而判斷錯誤發生的原因。
Hi,
謝謝你之前提出的資料,. 已經知道是google API 內Google Cloud Messaging出錯,現在沒有錯誤出現, 在手機上面可以收到通知ICON,但按下沒有任何訊息出現,還在找尋中.
那就祝你成功囉,這類debug多幾次就會有經驗了,GL!
多謝你的幫忙.
Hi, 不好意思, 修改程式後已經正常, 但中文字出亂碼?
之前以下上的網頁沒有顯示資料?請問有沒有其他網頁可參考.
http://caterpillar.onlyfun.net/Gossip/Encoding/Servlet.html 良葛格有一篇文章在講這件事情,參考看看吧。
網站搬家了,請參考這邊
http://openhome.cc/Gossip/Encoding/Servlet.html
Hi, 不好意思, 再要請教有關Server 怎麼設定, 我在網上 https://code.google.com/p/gcm/source/browse/ 下載了 gcm-76908409d9d5, 發覺gcm-demo-server 內是java 檔案,網頁不是html 或 php 的格式? 請問是否需要轉為網頁格式.
Hi,
一般如果是要與網頁服務整合,就Java的角度是不需要轉成網頁,而是透過網頁呼叫你上面提到的程式碼部分(如寫在Servlet),php的話我沒用過,但稍微看一下有人會透過GCM本身的RESTful API,使用curl的方式進行傳送,相關範例可以透過google “GCM PHP"等關鍵字找到,可以參考看看。
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寫好
謝謝你的資料,用了2星期時間,原來是提供俾我的user id 及權限問題. 已經解決可update 上database.
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.