2014年6月10日 星期二

如何向GCM Server傳送資料,以php、Java及JSP為例

向Google申請GCM成功,得到API Key、行動裝置也成功向GCM Server註冊得到Registration ID後,就可以開始由後台向GCM Server發送訊息,GCM成功拿到訊息之後,就會開始向對應Registration ID的行動裝置轉送訊息,當然,GCM Server並不會儲存Registration ID,Registration ID要由後台連同訊息一起傳給GCM Server才行。

以下的程式碼之先行作業為:
1.已向Google申請並得到API Key,假設為apiKey

2.行動裝置已經向GCM Server註冊得到了Registration ID,並已經將Registration ID傳給後台並存進資料庫中

3.資料庫此例為MySql,假設相關資訊為
   Host : localhost
   User : userName
   Password :  pass
   Database :  DB_Name
   Table      :   TB_Name
   Table只有一個欄位,名為regId,存放著Registration ID

一、先從php開始

<?php
// 這個php程式的作用為
// 發送 POST 給 Google GCM server,
// 並接收 Google GCM server 的回傳訊息,在某 Android 裝置已移除您的 app 時
// 做對應的處理

$apiKey = 'apiKey';                 //你向Google申請GCM服務時建立得到的ApiKey

$dbhost = 'localhost';
$dbuser = 'userName';           //你要用來連資料庫的User Name
$dbpass = 'pass';                   //對應User Name的密碼
$dbname = 'DB_Name';        //存放Registration ID的資料庫名稱

//建立資料庫連接
$conn = mysql_connect($dbhost, $dbuser, $dbpass) or die('Error with MySQL connection');
mysql_query("SET NAMES 'utf8'");
mysql_select_db($dbname);    //選擇資料庫

// 列出要發送的 user 端 Android 裝置
$sql = "SELECT regId FROM  TB_Name";
$rs = mysql_query($sql);

$recnt = mysql_num_rows($rs);    //計算紀錄數

// 總記錄數/每批發送數, 若不能整除, 則發送訊息迴圈數 +1
$SendMax = 1000;     // 每批發送訊息給 Android 的裝置數,GCM一次最大只能發1000台
$SendLoop = ceil($recnt/$SendMax);

for($x=0;$x<$SendLoop;$x++)
{
    $aRegID = array();    //建立一陣列,用來存放Registration ID,每一回合存1000個
   
    for($y=0;$y<$SendMax;$y++)
    {
        $index = ($x*$SendMax) + $y;    //index為第幾筆資料
        if($index<$recnt)
            {
                $row = mysql_fetch_row($rs);
                array_push($aRegID, $row[0]);   //0表示第一column,即regId的欄位            
            }
        else
            {
                break;
            }
    }    
    // $url為傳送之目的地之GCM Server
    $url = 'https://android.googleapis.com/gcm/send';
    //開使準備發送資訊
    //GCM接受多種資料型式,此列選擇JSON為發送型式,不同的發送型式須要在header中使用
    //不同的Content-Type
    // 假設要發送的訊息內容
    // 為message1,message2,message3三樣資訊
    // 以下建立JSON型式的資料
    $fields = array('registration_ids'  => $aRegID,
                           'data'                  => array( 'message1' => $message1,
                                                                     'message2' => $message2,
                                                                     'message3' => $message3,
                                                                  )
                          );
    //塞入header
    $headers = array('Content-Type: application/json',   //選擇用JSON型式發送資料時,Content-Type
                                                                                   //需設定為application/json
                               'Authorization: key='.$apiKey
                    );
    //開始發送訊息
    $ch = curl_init();
    // 設定發送目的($url)及發送方式(POST)和發送資料($headers和$fields)
    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_POSTFIELDS, json_encode($fields));
    // 送出 post, 並接收回應, 存入 $result
    $result = curl_exec($ch);
 
    echo $result;                    //可檢視GCM Server的回傳訊息

    // 由回傳結果, 取得已解除安裝的 regID
    // 自資料庫中刪除
    $aGCMresult = json_decode($result,true);
    $aUnregID = $aGCMresult['results'];
    $unregcnt = count($aUnregID);
    for($i=0;$i<$unregcnt;$i++)
    {
        $aErr = $aUnregID[$i];
        if($aErr['error']=='NotRegistered')
        {
            $sqlTodel = "DELETE FROM TB_Name          //將相對應的Registration ID從資料庫中刪除
                             WHERE regId='".$aRegID[$i]."' ";
            $rs2=mysql_query($sqlTodel);
            echo $rs2;
        }
    }
    //關閉連接
    curl_close($ch);
    // 刪除$aRegID變數
    unset($aRegID);
}
?>
二、Java的版本
以下的Java程式碼之先行作業為:
1.因為要用Java控制MySql,所以要先去MySql官網下載mysql-connector-java-5.0.8,解壓縮後將mysql-connector-java-5.0.8-bin.jar的路徑放到環境變數中。如果是在Linux的系統下,則是使用如下指令下載
apt-get install libmysql-java
之後可以在/usr/share/java/mysql.jar找到,把mysql.jar的路徑加到環境變數中的Classpath就可以了(更改/etc/profile檔案的內容)
2.在這邊為了方便使用POST的連線方法,我採用了Apache的HttpClient,到Apache的HttpClient下載頁面中下載HttpClient,解壓縮後將其內的jar路徑放到環境變數中。

3.因為在Java程式中用到了JSON的資料格式,所以要去JSON下載頁面Free source code is available連結中去下載,按下Download ZIP的按鈕即可下載。下載下來後解壓縮,可以看到在JSON-java-master資料夾內有許多的java檔,把這些java檔全部複製到專案中的org.JSON套件下即可使用,如果想要打把成jar檔使用的話,可以參考這篇文章,把所有java檔放到org.JSON的資料夾目錄下後,在org資料夾所處的資料中,使用命令列模式打上
javac ./org/json/*.java
jar -cvf json.jar ./org/json/*.class
後,就可以在org資料夾所處的資料中看到打包好的json.jar了
Java程式碼:
import java.lang.Class;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import org.apache.http.HttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.client.methods.*;
import org.apache.http.entity.StringEntity;
import org.json.JSONArray;
import org.json.JSONObject;

// 這個JAVA程式的作用為
// 發送 POST 給 Google GCM server,
// 並接收 Google GCM server 的回傳訊息
//在某 Android 裝置已移除您的 app 時
// 做對應的處理
public class GCM_send{
     public static void main(String[] args) {
          //驅動程序名
          String driver = "com.mysql.jdbc.Driver";
  // URL指向要訪問的數據庫名
  String url = "jdbc:mysql://127.0.0.1:3306/DB_Name";
  // MySQL配置時的用戶名
  String user = "userName";
  // Java連接MySQL配置時的密碼
  String password = "pass";
  Connection conn = null;
  // 加載驅動程序
      try{
         Class.forName(driver);
 // 連接數據庫
 conn = DriverManager.getConnection(url, user, password);
 if(!conn.isClosed()) System.out.println("Succeeded connecting to the Database!");
 // 使用statement用來執行SQL語句
 Statement statement = conn.createStatement();
 // 要執行的SQL語句
 String sql = "select regId from TB_Name";
                //計算裝置數目
                ResultSet rsCountRow = statement.executeQuery("select count(*) as COUNT from            
                                                                                              regid_table");
                rsCountRow.next();
double recnt = rsCountRow.getDouble("COUNT"); //得到裝置數目
//執行Registration ID的查詢
ResultSet rs = statement.executeQuery(sql);
 
double SendMax = 1000;                                         //一回最大傳送裝置限制
int SendLoop = (int) Math.ceil(recnt/SendMax); //計算要送幾回

//開始建立傳送資料及傳送給GCM Server
for(int x=0; x<SendLoop; x++){
     String regid;                           //行動裝置的Registration ID
                     建立Post連線設置
     HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
     CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
         HttpPost httpPost = new HttpPost("https://android.googleapis.com/gcm/send");
         //GCM服務的API Key                  
                     httpPost.addHeader("Authorization",
                                                     "key=apiKey");
       //選擇使用JSON型式傳送,在Header中加上Content-Type為application/json的參數
       httpPost.addHeader("Content-Type","application/json");
       //建立JSON內容
       JSONObject jsonObject = new JSONObject();   //要傳送的JSON物件
                    //因為Registration ID不止一個,要放在JSON Array中,再把JSON Array塞進JSON物
                    //件中
       JSONArray jsonArray = new JSONArray();    
   for(int y=0; y<SendMax; y++){
         if (rs.next()){
          regid = rs.getString("regId");            
              jsonArray.put(regid);      //將資料表中的各Registration ID放入jsonArray中
     }
    else{
          break;
    }
   }
   //填好JSONObject的各項值
   jsonObject.put("registration_ids",jsonArray);
   jsonObject.put("data.message1","message1");
   jsonObject.put("data.message2","message2");
   jsonObject.put("data.message3","message3");
      //建立StringEntity後使用Http Post送出JSON資料
   httpPost.setEntity(new StringEntity(jsonObject.toString()));
    HttpResponse response = closeableHttpClient.execute(httpPost);
    System.out.println(response.toString());
      //取得回傳的JSON格式資料
      String ss = EntityUtils.toString(response.getEntity(), "UTF-8");
      JSONObject jj = new JSONObject(ss);      
      System.out.println(jj);      
     
       JSONArray aUnregID = jj.getJSONArray("results");
       int unregcnt = aUnregID.length();
       for(int i=0;i<unregcnt;i++)
       {
            JSONObject aErr = aUnregID.getJSONObject(i);
        if(aErr.getString("error").equals("NotRegistered")){
                 rs.absolute(i+1);  //移到指定行
     String sqlTodel = "DELETE FROM  TB_Name WHERE regId='" + rs.getString("regId") + "'";
     satement.executeUpdate(sqlTodel);         //刪除沒安裝App裝置的Registration ID資料
            }
    }
      closeableHttpClient.close(); //關閉POST連線    
   }
      rs.close();
   conn.close();    //關閉資料庫連線
}
catch(Exception er){
System.out.println("Error:"+er.toString());
}
   }
}
二、JSP的版本
JSP基本上跟JAVA差不多,可以說在<%...%>中的Java程式碼跟Java版本的根本沒有什麼區別,不過當然這樣寫沒有用到JSP語法真正的好處,跟寫Servlet差不多,不過這就不在這邊多做討論。

在這邊的Tomcat佈署方式我是採取使用Netbeans IDE做程式的開發,注意需要將有用到的jar類別包正確的import進專案中,接著把Netbeans IDE所產生出來的war檔丟到Tomcat下webapps資料夾,再用瀏覽器打上正確路徑就可以了

而在Linux下也是一樣,我是在Windows下用Netbeans IDE先開發,再把war檔丟到Tomcat下webapps資料夾中,再用瀏覽器打上正確路徑就可以了

成功看到頁面後,會發現war檔會被折開成以你的專案名字名命的專案資料夾,裡面都已經部置好各個檔的位置了,包括import進來的包,其會在lib資料夾中

<%@page import="org.apache.http.util.EntityUtils"%>
<%@page import="org.apache.http.HttpResponse"%>
<%@page import="org.apache.http.entity.StringEntity"%>
<%@page import="org.json.JSONArray"%>
<%@page import="org.json.JSONObject"%>
<%@page import="org.apache.http.client.methods.HttpPost"%>
<%@page import="org.apache.http.impl.client.CloseableHttpClient"%>
<%@page import="org.apache.http.impl.client.HttpClientBuilder"%>
<%@ page contentType="text/html;charset=utf-8"%>
<%@ page import="java.sql.*"%>
<%
    try {
//讀取mysqlDriver驅動程式
        Class.forName("com.mysql.jdbc.Driver").newInstance();
//連接mysql資料庫

// 資料庫名稱"zend_test",帳號"root",密碼"123456",
// 使用Unicode編碼"true",字元集"UTF-8"
        String db_user = "userName";
        String db_pwd = "pass";
        String db_database = "DB_Name";
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/" + db_database + "?user=" + db_user + "&password=" + db_pwd + "&useUnicode=true&characterEncoding=UTF-8");

//建立statement
        Statement statement = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

//建立SQL查詢
        String sql = "select regId from TB_Name";
        //計算裝置數目
        ResultSet rsCountRow = statement.executeQuery("select count(*) as COUNT from TB_Name");
        rsCountRow.next();

        double recnt = rsCountRow.getDouble("COUNT"); //得到裝置數目
        //執行Registration ID的查詢

        double SendMax = 1000;                                         //一回最大傳送裝置限制
        int SendLoop = (int) Math.ceil(recnt / SendMax); //計算要送幾回
        ResultSet rs = statement.executeQuery(sql);
        //開始建立傳送資料及傳送給GCM Server
        for (int x = 0; x < SendLoop; x++) {
            String regid;                           //行動裝置的Registration ID
            //建立Post連線設置
            HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
            CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
            HttpPost httpPost = new HttpPost("https://android.googleapis.com/gcm/send");
            //GCM服務的API Key                  
            httpPost.addHeader("Authorization", "key=apiKey");
            //選擇使用JSON型式傳送,在Header中加上Content-Type為application/json的參數
            httpPost.addHeader("Content-Type", "application/json");
            //建立JSON內容
            JSONObject jsonObject = new JSONObject();   //要傳送的JSON物件
            //因為Registration ID不止一個,要放在JSON Array中,再把JSON Array塞進JSON物
            //件中
            JSONArray jsonArray = new JSONArray();
            for (int y = 0; y < SendMax; y++) {
                if (rs.next()) {
                    regid = rs.getString("regId");
                    jsonArray.put(regid);      //將資料表中的各Registration ID放入jsonArray中
                } else {
                    break;
                }
            }
            //填好JSONObject的各項值
            jsonObject.put("registration_ids", jsonArray);
            jsonObject.put("data", new JSONObject().put("message","testMessage");
            //建立StringEntity後使用Http Post送出JSON資料
            httpPost.setEntity(new StringEntity(jsonObject.toString()));
            HttpResponse httpResponse = closeableHttpClient.execute(httpPost);
            System.out.println(httpResponse.toString());
            //取得回傳的JSON格式資料
            String ss = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
            JSONObject jj = new JSONObject(ss);
            out.println(jj.toString());

            JSONArray aUnregID = jj.getJSONArray("results");
            int unregcnt = aUnregID.length();
            for (int i = 0; i < unregcnt; i++) {
                JSONObject aErr = aUnregID.getJSONObject(i);
                if (aErr.getString("error").equals("NotRegistered")) {
                    rs.absolute(i + 1);  //移到指定行
                    String sqlTodel = "DELETE FROM TB_Name WHERE regId='" + rs.getString("regId") + "'";
                    statement.executeUpdate(sqlTodel);  //刪除沒安裝App裝置的Registration ID資料
                }
            }
        }
%>
<%
// 關閉連線
        rs.close();
        rs = null;
        statement.close();
        statement = null;
        conn.close();
    } catch (Exception e) {
        out.println(e.toString());
    }
%>

沒有留言 :

張貼留言