Google Cloud Messaging(GCM) 使用

2015-08-22

error

error

這篇要來介紹的是 如何使用GCM推播服務,我們要完成上圖,按下按鈕註冊Token,按下發送,Push一則推播給自己的裝置,在Coding之前有幾件事情要先做:
●到你的Google Api主控台
●建立一個新專案
●開啟Cloud Messaging for Android Api服務
●新增伺服器憑證
●取得Sender ID 與 伺服器Api Key
(藍字部份是因為這個Demo要發送Push給自己,若只想單純接收而已可以忽略)
下面會有圖文介紹,若還有疑問可以來看官方說明



1.到Google Api主控台

建立一個新專案

error

點選首頁 -> 記下Sender ID

error

點選API -> 找到Cloud Messaging for Android -> 進入設定為啟動

error

點選憑證 -> 新增憑證 -> Api金鑰 -> 伺服器金鑰 -> 不用輸入東西直接建立

error

記下Api Key

error

2.可以開始寫Code了


Demo專案介紹:

MainActivity(主畫面)
GcmService(接收Push的背景服務)
RegisterTask(註冊Token的非同步任務)
SendPushTask(發送Push的非同步任務)
activity_main(主畫面Layout)

error

3.Add Play-Service Lib


打開你的gradle,引用Lib

error
compile 'com.google.android.gms:play-services:7.5.0'


4.Layout


activity_main這個layout很簡單,就不再做介紹了

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="10dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="我的 Token :"
            />

        <TextView
            android:textColor="#FF0000"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="空"
            android:id="@+id/tokenTv"
            />
    </LinearLayout>


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="註冊 TOKEN"
        android:id="@+id/registerBtn"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="10dp">
        <EditText
            android:id="@+id/messageEt"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:hint="這裡輸入要發給自己的內容"
            />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="發送"
            android:id="@+id/sendPushBtn"
            />
    </LinearLayout>
</LinearLayout>


5.Java Code


先來看MainActivity

15.16列 請記得改成剛剛第一步記下來的Sender ID與Api Key
52.53列 JSONObject 的 key 是GCM api的固定格式,不能改唷!

package com.anson.pushtest;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class MainActivity extends Activity {
    public static final String GCM_SENDER_ID = "你的Sender ID";
    public static final String SERVER_API_KEY = "你的 Api Key";
    public static final String TEST_MY_MESSAGE_KEY = "MY_PUSH_MESSAGE";

    private TextView mTokenTv;
    private Button mRegisterBtn , mSendPushBtn;
    private EditText mMessageEt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTokenTv = (TextView)findViewById(R.id.tokenTv);
        mRegisterBtn = (Button)findViewById(R.id.registerBtn);
        mSendPushBtn = (Button)findViewById(R.id.sendPushBtn);
        mMessageEt = (EditText)findViewById(R.id.messageEt);

        mRegisterBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new RegisterTask(MainActivity.this,mTokenTv).execute();
            }
        });
        mSendPushBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String targetToken = mTokenTv.getText().toString();
                String message = mMessageEt.getText().toString();
                new SendPushTask(MainActivity.this).execute(getPushMessage(targetToken,message));
            }
        });
    }

    /**
     * 取得推波訊息格式
     */
    private static String getPushMessage(String targetToken,String message) {
        String KEY_REGISTRATION_IDS = "registration_ids";
        String KEY_DATA = "data";

        JSONObject jObj = new JSONObject();
        JSONArray idsArray = new JSONArray();
        idsArray.put(targetToken);

        JSONObject jObjData = new JSONObject();
        try {
            jObjData.put(TEST_MY_MESSAGE_KEY,message);

            jObj.put(KEY_REGISTRATION_IDS, idsArray);
            jObj.put(KEY_DATA, jObjData);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return jObj.toString();
    }
}

註冊任務 RegisterTask

package com.anson.pushtest;

import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.gcm.GoogleCloudMessaging;

/**
 * Created by Anson on 2015/8/22.
 */
public class RegisterTask extends AsyncTask<Void, Void, String> {
    private ProgressDialog mDialog;
    private Context mContext;
    private TextView mTokenTv;

    public RegisterTask(Context context,TextView tokenTv) {
        mDialog = new ProgressDialog(context);
        mDialog.setTitle("提示");
        mDialog.setMessage("註冊Token中~~~~");
        mDialog.setCancelable(false);

        mContext = context;
        mTokenTv = tokenTv;
    }

    @Override
    protected void onPreExecute() {
        mDialog.show();
        super.onPreExecute();
    }

    @Override
    protected String doInBackground(Void... params) {
        String token = null;
        try {
            token = GoogleCloudMessaging.getInstance(mContext).register(MainActivity.GCM_SENDER_ID);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return token;
    }


    @Override
    protected void onPostExecute(String token) {
        if (token == null) {
            //註冊token失敗
            Toast.makeText(mContext,"註冊失敗,請檢查你的 SENDER_ID 是否有設定正確",Toast.LENGTH_LONG).show();
        }else {
            mTokenTv.setText(token);
        }
        mDialog.cancel();
        super.onPostExecute(token);
    }
}

發送任務 SendPushTask

package com.anson.pushtest;

import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.Toast;

import java.io.BufferedWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Created by Anson on 2015/8/22.
 */
public class SendPushTask extends AsyncTask<String, Void, Integer> {
    private ProgressDialog mDialog;
    private Context mContext;

    public SendPushTask(Context context) {
        mDialog = new ProgressDialog(context);
        mDialog.setTitle("提示");
        mDialog.setMessage("發送Push中~~");
        mDialog.setCancelable(false);
        mContext = context;
    }

    @Override
    protected void onPreExecute() {
        mDialog.show();
        super.onPreExecute();
    }

    @Override
    protected Integer doInBackground(String... params) {
        int statusCode = -1;
        try {
            URL url = new URL("https://android.googleapis.com/gcm/send");

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(10000);
            conn.setConnectTimeout(10000);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setRequestProperty("Authorization", "key = "+ MainActivity.SERVER_API_KEY);
            conn.setDoInput(true);
            conn.setDoOutput(true);

            OutputStream os = conn.getOutputStream();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
            writer.write(params[0]);
            writer.flush();
            writer.close();
            os.close();

            conn.connect();
            statusCode = conn.getResponseCode();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return statusCode;
    }


    @Override
    protected void onPostExecute(Integer statusCode) {
        if(statusCode!=200){
            Toast.makeText(mContext,"發送失敗,請檢查你的 SERVER_API_KEY 是否有設定正確",Toast.LENGTH_LONG).show();
        }
        mDialog.cancel();
        super.onPostExecute(statusCode);
    }
}

推播訊息監聽服務 GcmService

package com.anson.pushtest;

import android.os.Bundle;
import com.google.android.gms.gcm.GcmListenerService;


/**
 * Created by Anson on 2015/8/22.
 */
public class GcmService extends GcmListenerService{
    @Override
    public void onMessageReceived(String from, Bundle data) {
        String message = data.getString(MainActivity.TEST_MY_MESSAGE_KEY);
        System.out.println("收到推播了-from:" + from);
        System.out.println("收到推播了-data:" + message);
    }
}


6.Manifest的設定


這邊要特別注意一下,裡面的所有package name都要記得改成你的

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="[你的package_name]" >
    <!-- 網路權限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 休眠喚醒權限 -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <!-- 推播權限 -->
    <permission
        android:name="[你的package_name].permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="[你的package_name].permission.C2D_MESSAGE" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <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="com.google.android.gms.gcm.GcmReceiver"
            android:exported="true"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="[你的package_name]" />
            </intent-filter>
        </receiver>
        <service
            android:name=".GcmService"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </service>
    </application>
</manifest>


7.完成了!來看效果吧


1.先按註冊Token
2.完成後會看到紅字部分改變了
3.這時就可以發送訊息給自己囉!

error

8.總結


1. GCM 僅提供上限 4kb 的輕量資料傳遞
2. 註冊完的Token 時間久了是有可能改變(失效)的,但我目前使用起來還沒有出現這個問題
3. 若App在尚未刪除狀態下,重新註冊Token,有有可能會是一組不同的 Token,但也有可能一樣
4. 若App在刪除後重新安裝,重新註冊Token,這時會是一組新的 Token



都看到最後了,代表你還蠻有上進心的XD