google的GCM推送使用簡介
轉載請註明出處:http://blog.csdn.net/newhope1106/article/details/54709916
GCM即Google Cloud Messaging,主要用於訊息推送的,即使在應用沒有起來的情況下,客戶端也能通過GCM收到來自伺服器的訊息。GCM支援Android、IOS和Chrome。由於GCM需要google service支援,在國內基本不能用,經常會斷線,不過最近專案要求,只在美國上線該專案,因此可以採用GCM實現推送的方式,國內相關文章較少,特意整理了一下客戶端使用的官方文件。
首先來看看目前應用不啟動實現推送的方式有哪些:
1.使用Google自帶的GCM實現推送
2.採用監聽開機廣播的方式,啟動後臺服務,為了防止被殺死,採用多程序的方式,監聽服務是否被殺,被殺之後,把它拉起來(比較流氓)
3.採用第三發方案,如友盟、極光、信鴿
國內第二種和第三種用得比較多,本文主要講解第一種方案,也就是GCM,以下內容均來自官網
一、概述
GCM可以讓開發者在客戶端和伺服器之間傳遞訊息,有2種方式實現訊息推送,一種是xmpp,它即可讓伺服器把訊息推送給客戶端,也可讓客戶端把訊息推送給伺服器,另一種方式是http,只能伺服器將訊息推送給客戶端,以下是其架構圖。
GCM伺服器接收應用伺服器的訊息,然後再把訊息轉發給客戶端,伺服器端根據自己的需要實現xmpp或者http介面,和GCM伺服器進行通訊,客戶端想要接收訊息,需要使用GCM提供的API。
二.客戶端使用GCM
1.使用限制:
a.最低要求Android 2.2+的裝置,並且安裝了Google應用商店
b.想要使用GCM新特性,要求Android 2.3+
c.低於Android 4.0.4版本,需要Google賬號,Android 4.0.4+不需要
2.客戶端使用GCM流程
和使用一般的sdk類似,首先需要在官網註冊自己的應用,獲取一個appid,出於安全要求,需要在本地使用這個appid去獲取動態token,需要把token上傳給伺服器,每隔一段時間token可能會失效,需要去重新獲取token。按照上面的過程我們來看看怎麼使用的。以下針對Android Studio開發的。
(1)官網註冊應用
首先我們需要到這個網址:https://console.firebase.google.com/ 去註冊自己的應用,按照相關步驟操作之後,點選下載配置檔案,會下載一個叫google-services.json的檔案,把它放到自己的專案的app/目錄下。
(2)新增配置檔案解析外掛依賴
上述下載的google-services.json需要外掛進行解析,要在專案中按照下面步驟新增依賴
a.在專案級別(project-level)的build.gradle新增下面依賴
classpath 'com.google.gms:google-services:3.0.0'
b.在自己的應用級別(app-level)的build.gradle新增下面外掛
apply plugin: 'com.google.gms.google-services'
(3)新增GCM依賴
程式碼中需要用到gcm的api,因此需要新增gcm的依賴,版本請使用最新的版本,下面只是示範
dependencies {
compile "com.google.android.gms:play-services-gcm:10.0.0"}
(4)修改AndroidMenifest.xml檔案
a.新增許可權,因為我們不允許其他的應用接收和傳送屬於自己應用的訊息,因此新增許可權遮蔽其他應用,許可權格式:<application-package-name> + ".permission.C2D_MESSAGE"
b.定義一個GcmReceiver接收器,用來接收發送給應用的訊息,需要新增com.google.android.c2dm.permission.SEND許可權
c.定義GcmListenerService伺服器,用來處理各種不同的下發資訊,上髮狀態,自動顯示通知等
d.定義一個整合InstanceIDListenerService的服務,用來獲取、重新整理token
e.額外的,可以新增android.permission.WAKE_LOCK許可權,保證訊息到達的時候,可以得到及時處理
以下是一個demo
<manifestpackage="com.example.gcm" ...><uses-sdkandroid:minSdkVersion="8"android:targetSdkVersion="17"/><uses-permissionandroid:name="android.permission.WAKE_LOCK"/>
<permission android:name="<your-package-name>.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="<your-package-name>.permission.C2D_MESSAGE" />
<application ...><receiverandroid:name="com.google.android.gms.gcm.GcmReceiver"android:exported="true"android:permission="com.google.android.c2dm.permission.SEND"><intent-filter><actionandroid:name="com.google.android.c2dm.intent.RECEIVE"/><categoryandroid:name="com.example.gcm"/></intent-filter></receiver><serviceandroid:name="com.example.MyGcmListenerService"android:exported="false"><intent-filter><actionandroid:name="com.google.android.c2dm.intent.RECEIVE"/></intent-filter></service><serviceandroid:name="com.example.MyInstanceIDListenerService"android:exported="false"><intent-filter><actionandroid:name="com.google.android.gms.iid.InstanceID"/></intent-filter></service><serviceandroid:name="gcm.play.android.samples.com.gcmquickstart.RegistrationIntentService"android:exported="false"></service></application></manifest>
如果是android 4.4之前的版本,需要在receiver中新增的intent-filter中新增下面的action
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
3.客戶端獲取、重新整理token
(1)先來看看獲取token的介面
String authorizedEntity = PROJECT_ID;// Project id from Google Developer ConsoleString scope ="GCM";// e.g. communicating using GCM, but you can use any// URL-safe characters up to a maximum of 1000, or// you can also leave it blank.String token =InstanceID.getInstance(context).getToken(authorizedEntity,scope);
(2)token的更新
在前面有提到AndroidMenifest.xml中註冊一個繼承InstanceIDListenerService的服務,看看具體的實現
publicclassMyInstanceIDServiceextendsInstanceIDListenerService{publicvoid onTokenRefresh(){
refreshAllTokens();}privatevoid refreshAllTokens(){// assuming you have defined TokenList as// some generalized store for your tokensArrayList<TokenList> tokenList =TokensList.get();InstanceID iid =InstanceID.getInstance(this);for(tokenItem : tokenList){
tokenItem.token =
iid.getToken(tokenItem.authorizedEntity,tokenItem.scope,tokenItem.options);// send this tokenItem.token to your server}}}
第一次獲取token,雖然沒有獲取過token,但本質還是重新整理,都是呼叫onTokenRefresh介面,並且把獲取的token傳送給伺服器。
上面的處理可以最好用一個IntentService來非同步處理,不要放在主執行緒中,上述給一個使用範例而已。
(3)InstanceID
上面我們看到,獲取token的時候,首先需要InstanceID,當裝置上線的時候,Instance ID Service會分配一個InstanceID, InstanceID是由一對公鑰和私鑰共同維護的,私鑰儲存在本地,公鑰由Instance ID Service註冊產生。可以通過呼叫geId()方法,隨時更新InstanceId,因為生成的token都是依賴這個InstanceID的。
String iid =InstanceID.getInstance(context).getId();
你也可以刪除一個InstanceID,那麼對應的所有token都會失效,用新的InstanceID生成新的token
InstanceID.getInstance(context).deleteInstanceID();String newIID =InstanceID.getInstance(context).getId();
(4)Instance ID 的生命週期圖
4.客戶端接收伺服器下發資訊
a.伺服器可以通過HTTP(單向)和XMPP(雙向)傳送資訊,看看下面的demo
HTTP POST Request
https://gcm-http.googleapis.com/gcm/sendContent-Type:application/jsonAuthorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA{"data":{"score":"5x1","time":"15:10"},"to":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."}
XMPP Message
<messageid=""><gcmxmlns="google:mobile:data">{ "data": {
"score": "5x1",
"time": "15:10"
},
"to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
}
</gcm></message>
b.客戶端處理下發訊息
伺服器傳送的訊息,GCM會將接收到的訊息轉發給客戶端,在前面的AndroidManifest.xml中,我們定義了一個GcmListenerService來處理訊息,可以自己繼承GcmListenerService並且覆蓋onMessageReceived方法。
@Overridepublicvoid onMessageReceived(Stringfrom,Bundle data){String message = data.getString("message");Log.d(TAG,"From: "+from);Log.d(TAG,"Message: "+ message);if(from.startsWith("/topics/")){// message received from some topic.}else{// normal downstream message.}// ...}
5.客戶端上發訊息
publicvoid onClick(finalView view){if(view == findViewById(R.id.send)){newAsyncTask<void, void,="" string="" style="box-sizing: inherit;">(){@OverrideprotectedString doInBackground(Void...params){String msg ="";try{Bundle data =newBundle();
data.putString("my_message","Hello World");
data.putString("my_action","SAY_HELLO");String id =Integer.toString(msgId.incrementAndGet());
gcm.send(SENDER_ID +"@gcm.googleapis.com", id, data);
msg ="Sent message";}catch(IOException ex){
msg ="Error :"+ ex.getMessage();}return msg;}@Overrideprotectedvoid onPostExecute(String msg){
mDisplay.append(msg +"\n");}}.execute(null,null,null);}elseif(view == findViewById(R.id.clear)){
mDisplay.setText("");}}</void,>
主要是利用了gcm的send介面,上述需要伺服器支援xmpp
6.伺服器端的實現
有興趣的可以參考google的程式碼demo