Google 的service之一, Google Cloud Messaging (GCM) 訊息推播, 主要在讓我們省去架設自己的通訊伺服器, 其運作流程框架如圖
► 首先有Google帳號並啟用 GCM 服務
1. 啟用 Google APIs Consols , 開啟 GCM 服務
3. 建立專案, 產生 Server Key
► GCM for Android
引用的 api 有兩種, 一為 gcm.jar(非常久性支援,但檔案較小), 一為 google-play-services.jar(google 後續的主推方式, 從SDK Manage 內直接下載), 兩種處理程序幾乎完全相同. 下為使用 play-services 方式
AndroidManifest.xml 必要參數設定
AndroidManifest.xml 必要參數設定
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package=[AppPackage名稱] android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> <permission android:name="[AppPackage名稱].permission.C2D_MESSAGE" android:protectionLevel="signature" /> <uses-permission android:name="[AppPackage名稱].permission.C2D_MESSAGE" /> <uses-permission android:name="[AppPackage名稱].c2dm.permission.RECEIVE" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".GCMBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" > <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <category android:name="[AppPackage名稱]" /> </intent-filter> </receiver> <service android:name=".GCMIntentService" /> </application> </manifest>
GCMBroadcastReceiver.java 接收外部訊息的進入點
public class GCMBroadcastReceiver extends WakefulBroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { ComponentName comp = new ComponentName(context.getPackageName(), GCMIntentService.class.getName()); startWakefulService(context, (intent.setComponent(comp))); setResultCode(Activity.RESULT_OK); } }
GCMIntentService.java 處理訊息
► GCM Server for PHPpublic class GCMIntentService extends IntentService { public GCMIntentService() { super("GCMIntentService");//傳遞名稱用意?? } @Override protected void onHandleIntent(Intent intent) { Bundle extras = intent.getExtras();//訊息均封裝在此 GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this); String msgType = gcm.getMessageType(intent); if (!extras.isEmpty()) { if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(msgType)) { Log.d(TAG,"Type[ERROR]: " + msgType + ",body:" + extras.toString()); } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(msgType)) { Log.d(TAG,"Type[DELETED]: " + msgType + ",body:" + extras.toString()); } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(msgType)) { Log.d(TAG,"Type[MESSAGE]: " + msgType + ",body:" + extras.toString()); } } GCMBroadcastReceiver.completeWakefulIntent(intent); } }
Server 端通常需要兩個程序, 一個讓 Android裝置進行登錄自己的 Registration ID, 這樣 Server 才知道要發送訊息給哪些裝置. 另一個就是發送訊息的程序, 但注意的是目前資料有 4K 限制, 所以當發送的對象多(rID)時, 宜採分批發送避免被 Google GCM捨棄.
訊息發送格式
訊息發送格式
$url = 'https://android.googleapis.com/gcm/send';// Googlo GCM Server $fields = array(//資料格式 'registration_ids' => array('rId','rId'),//對象 'data' => array("message" => $message ),//訊息 ); $headers = array(//Server Key 'Content-Type: application/json', 'Authorization: key=' . $serverKey,//API Key ); // 向Google GCM Server 發送訊息 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields)); $response = curl_exec($ch);//GCM Server 回覆結果 if ($response === FALSE) { die('Problem occurred: ' . curl_error($ch)); } $info = curl_getinfo($ch); curl_close($ch); $state = $info['http_code']; if ($state == 200) { //echo $response . ''; $json_response = json_decode($response); $multicast_id = $json_response->multicast_id; $success = $json_response->success; $failure = $json_response->failure; $canonical_ids = $json_response->canonical_ids; echo "GCM request $multicast_id was successful: "; echo "$success successful messages "; echo "$failure failed messages "; echo "$canonical_ids canonical ids "; gcm_results($fields['registration_ids'], $json_response->results); } else if ($state == 400) { echo 'GCM JSON error'; } else { // 500 or 503 echo "GCM error code $state"; } function gcm_results($registration_ids, $results) { echo '<b>Result list:</b><hr>'; foreach ($results as $key => $result) { echo 'TargetId=' . $registration_ids[$key] . ' '; if (isset($result->error) && !empty($result->error)) { echo '<b>error=' .$result->error . '</b>'; // 無效ID } else { echo '<b>Send Ok</b>:'.$result->message_id . ''; } } }
回覆格式如下(JSON)
{
"multicast_id":6704309358101356778, "success":1, "failure":1, "canonical_ids":0,
"results":[
{"error":"InvalidRegistration"},
{"message_id":"0:1392177391536659%2bf58953f9fd7ecd"}
]
}
►GCM Server for Delphi
"multicast_id":6704309358101356778, "success":1, "failure":1, "canonical_ids":0,
"results":[
{"error":"InvalidRegistration"},
{"message_id":"0:1392177391536659%2bf58953f9fd7ecd"}
]
}
OpenSSL http://indy.fulgan.com/SSL/const sendUrl = 'https://android.googleapis.com/gcm/send'; var lRequest: TStringStream; response: String;//server 回應內容 IdSSLIO: TIdSSLIOHandlerSocketOpenSSL; begin //需要 libeay32.dll, ssleay32.dll,至於程式目錄下即可 IdSSLIO := IdSSLIOHandlerSocketOpenSSL1; IdSSLIO.SSLOptions.Method := sslvSSLv3; IdSSLIO.SSLOptions.Mode := sslmUnassigned; lRequest := TStringStream.Create('{"registration_ids":["rID1","rID2"],"data":{"message":"訊息"}}', TEncoding.UTF8); with IdHTTP1 do begin IOHandler := IdSSLIO;//可直接用元件連接 ConnectTimeout := 3000; HTTPOptions := [hoKeepOrigProtocol,hoForceEncodeParams];//一定要 hoKeepOrigProtocol Request.ContentType := 'application/json'; Request.CharSet := 'UTF-8'; Request.CustomHeaders.Clear; Request.CustomHeaders.Add('Content-Type: application/json'); Request.CustomHeaders.Add('Authorization: key='+ServerAPIKey); Response.CharSet := 'UTF-8'; end; try IdHTTP.response := Post(sendUrl, lRequest); except end; lRequest.Free; IdHTTP.Disconnect; end;