2014년 8월 10일 일요일

Android Google Cloud Messaging Tutorial

android-gcm-archGoogle Cloud Messaging for Android (GCM) is a push-notification-like service freely provided by Google. Simply, it allows you to send data from one-end “we will call it a GCM server” to GCM clients e.g. Android app.
This service can be used to send notifications or messages containing up to 4Kb of payload data. The GCM service takes care of messages queueing and delivery to the target application. GCM is completely free no matter how big your messaging needs are, and there are “so far” no quotas.

Objective:

Building a complete example showing how to enable GCM service, how to build GCM client “Android app” and how to build GCM server.

Environment & Tools:

  • Eclipse ADT
  • Emulator AVD, target: Google APIs – API Level 19

Library:

  • Jackson library to convert Java object to JSON

About the tutorial:

This tutorial is trying to cover all the needed steps to build a complete working example. As shown in the picture above we need to go over 4 steps:
  1. Go to Google Developers Console
  2. Build GCM Client “Android App”
  3. Build GCM Server “Java App”
  4. Run & Test
    • a. Register the Android app with GCM to receive Registration Id “RegId
    • b. Share “RegId” with the server
    • c. Send a message from server to GCM using HTTP POST request

( 1 ) Go to Google Developers Console

Google Developers Console is a place where you enable “activate” Google services that you want to integrate in your application. In this console you need to do the following:
  • 1.1 Create a Project
  • 1.2 Get Project Number
  • 1.3 Enable Google Cloud Messaging for Android
  • 1.4 Create Server API Key
  • 1.1 Create a Project

If this is the first time you visit Google Developer Console, you need to create a Project “A project consists of a set of applications, along with activated APIs, Google Cloud resources, and the team and billing information associated with those resources.”
  • Go to Developer Console home page
  • Click “CREATE PROJECT” button
  • Fill Project name “HMKCODE GCM”  & Project ID “hmkcode-gcm-project
  • 1.2 Get Project Number

    • Go to Developer Console home page
    • Click on the project name “HMKCODE GCM
    • At the top of the page yo will see Project Number
    • We we will need this number in Android app
  • 1.3 Enable Google Cloud Messaging for Android

    • Under the same project “HMKCODE GCM”
    • Click on API & auth >> API
    • Enable Google Cloud Messaginf for Android
  • 1.4 Create Serve API Key

  • Go to API & auth >> Credentials
  • Under Public API access click CREATE NEW KEYSelect Server Key
  • Click Create “leave the text box empty if you don’t have a specific IP address”
  • The Server API Key will be created
  • We will need this key in GCM Server

( 2 ) Build GCM Client “Android App”

  • 2.1 Create a new Android Project
  • 2.2 Create a Layout for MainActivity
  • 2.3 MainActivity.java
  • 2.4 GcmBroadcastReceiver.java
  • 2.5 GcmMessagerHandler.java
  • 2.6 AndroidManifest.xml
  • 2.1 Create a new Android Project

    • Application Name: Android GCM
    • Project Name: android-gcm-client
    • Package Name: com.hmkcode.android.gcm
  • 2.2 Create Layout for MainActivity

The layout is so simple for this example. One Button to request registration ID and oneEditText to display the registration ID “RegID
android-gcm-client-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"
    tools:context=".MainActivity" 
    android:orientation="vertical" >   

    <Button 
        android:id="@+id/btnGetRegId"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Get RegId"/>

    <EditText
        android:id="@+id/etRegId"
        android:layout_width="match_parent"
        android:layout_height="250dp" />

</LinearLayout>
  • 2.3 MainActivity.java

  • /src/com/hmkcode/android/gcm/MainActivity.java
This class has three functions
  • onCreate() : Loads the layout and get reference to Button and EditText
  • getRegId(): AsyncTask will register the app with GCM, receive registration id “RegID” & display it on EditText. Here you need to use the Project Number when communicating with GCM
  • onClick(): Listens to Button click event and call getRegId()
package com.hmkcode.android.gcm;

import java.io.IOException;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends Activity implements OnClickListener {

    Button btnRegId;
    EditText etRegId;
    GoogleCloudMessaging gcm;
    String regid;
    String PROJECT_NUMBER = "102488860000";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnRegId = (Button) findViewById(R.id.btnGetRegId);
        etRegId = (EditText) findViewById(R.id.etRegId);

        btnRegId.setOnClickListener(this);
    }
    public void getRegId(){
        new AsyncTask<Void, Void, String>() {
            @Override
            protected String doInBackground(Void... params) {
                String msg = "";
                try {
                    if (gcm == null) {
                        gcm = GoogleCloudMessaging.getInstance(getApplicationContext());
                    }
                    regid = gcm.register(PROJECT_NUMBER);
                    msg = "Device registered, registration ID=" + regid;
                    Log.i("GCM",  msg);

                } catch (IOException ex) {
                    msg = "Error :" + ex.getMessage();

                }
                return msg;
            }

            @Override
            protected void onPostExecute(String msg) {
                etRegId.setText(msg + "\n");
            }
        }.execute(null, null, null);
    }
    @Override
    public void onClick(View v) {
        getRegId();
    } }
  • 2.4 GcmBroadcastReceiver.java

  • /src/com/hmkcode/android/gcm/GcmBroadcastReceiver.java
This is class will receive the GCM message & pass it to the GcmMessageHandler.
package com.hmkcode.android.gcm;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;

public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        // Explicitly specify that GcmMessageHandler will handle the intent.
        ComponentName comp = new ComponentName(context.getPackageName(),
                GcmMessageHandler.class.getName());

        // Start the service, keeping the device awake while it is launching.
        startWakefulService(context, (intent.setComponent(comp)));
        setResultCode(Activity.RESULT_OK);
    }
}
  • 2.5 GcmMessageHandler.java

  • /src/com/hmkcode/android/gcm/GcmMessageHandler.java
This class defines what to do with the received message. The received message will contain in its data part “as we will see later in GCM server step” two attributes title & message, we will extract “title” value from the intent extras & display it on a Toast.
{ 
  "data": {
    "title": "Test Title",
    "message": "Test Message"
  },
  ..... 
}
package com.hmkcode.android.gcm;

import com.google.android.gms.gcm.GoogleCloudMessaging;
import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;

public class GcmMessageHandler extends IntentService {

     String mes;
     private Handler handler;
    public GcmMessageHandler() {
        super("GcmMessageHandler");
    }

    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
        handler = new Handler();
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        Bundle extras = intent.getExtras();

        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
        // The getMessageType() intent parameter must be the intent you received
        // in your BroadcastReceiver.
        String messageType = gcm.getMessageType(intent);

       mes = extras.getString("title");
       showToast();
       Log.i("GCM", "Received : (" +messageType+")  "+extras.getString("title"));

        GcmBroadcastReceiver.completeWakefulIntent(intent);

    }

    public void showToast(){
        handler.post(new Runnable() {
            public void run() {
                Toast.makeText(getApplicationContext(),mes , Toast.LENGTH_LONG).show();
            }
         });

    }
}
  • 2.6 AndroidManifest.xml

You need to add five permissions, a receiver and service tags.
  • Permission com.google.android.c2dm.permission.RECEIVE .
  • Permission android.permission.INTERNET
  • Permission android.permission.GET_ACCOUNTS
  • Permission android.permission.WAKE_LOCK
  • Permission applicationPackage + ".permission.C2D_MESSAGE"
  • Receiver name=com.hmkcode.android.gcm.GcmBroadcastReceiver
  • Service name=com.hmkcode.android.gcm.GcmMessageHandler
  • Meta-data for Google Play service version
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.hmkcode.android.gcm"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <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="com.hmkcode.android.gcm.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="com.hmkcode.android.gcm.permission.C2D_MESSAGE" />

    <application

        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" 
        >
        <activity
            android:name="com.hmkcode.android.gcm.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="com.hmkcode.android.gcm" />
            </intent-filter>
        </receiver>
        <service android:name=".GcmMessageHandler" />

        <meta-data android:name="com.google.android.gms.version"
           android:value="@integer/google_play_services_version" />
    </application>

</manifest>

 ( 3 ) Build GCM Server “Java App”

GCM server can be an app “web, desktop or even mobile app”, or scheduled scripts that can establish HTTP connection with GCM and then send POST request instructing GCM to send a message to one or more registered GCM clients “mobile apps”.
The HTTP POST request should include two headers:
  • Authorization: key=SERVER API KEY
  • Content-Typeapplication/json for JSON; application/x-www-form-urlencoded;charset=UTF-8 for plain text.
HTTP POST body can be in JSON or plain text format. In this example we will use JSONformat. Below is an example of what we will send in body of the request.
{ 
  "data": {
    "title": "Test Title",
    "message": "Test Message"
  },
  "registration_ids": ["RegId", "another RegId if needed"] 
}
  • 3.1 Create a new Java Project
  • 3.2 Content.java
  • 3.3 POST2GCM.java
  • 3.4 App.java
  • 3.1 Create a new Java Project

  • 3.2 com/hmkcode/vo/Contnet.java

This is a simple POJO to hold POST body content “will be converted to JSON when sending the request”. Here we need to pass the following:
  1. RegIdsOne or more RegId can be added
  2. title & message: Two strings which will form the data part of the content.
package com.hmkcode.vo;

import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class Content implements Serializable {

    private List<String> registration_ids;
    private Map<String,String> data;

    public void addRegId(String regId){
        if(registration_ids == null)
            registration_ids = new LinkedList<String>();
        registration_ids.add(regId);
    }

    public void createData(String title, String message){
        if(data == null)
            data = new HashMap<String,String>();

        data.put("title", title);
        data.put("message", message);
    }
  • 3.3 com/hmkcode/POST2GCM.java

This class will send the POST request. Here we need to pass the following:
  1. Server API Key: this key will be added to POST headers.
  2. Content object which will be converted in JSON format using Jackson library
package com.hmkcode;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
 import com.fasterxml.jackson.databind.ObjectMapper;
import com.hmkcode.vo.Content;

public class POST2GCM {

    public static void post(String apiKey, Content content){

        try{

        // 1. URL
        URL url = new URL("https://android.googleapis.com/gcm/send");

        // 2. Open connection
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

        // 3. Specify POST method
        conn.setRequestMethod("POST");

        // 4. Set the headers
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestProperty("Authorization", "key="+apiKey);

        conn.setDoOutput(true);

            // 5. Add JSON data into POST request body

            //`5.1 Use Jackson object mapper to convert Contnet object into JSON
            ObjectMapper mapper = new ObjectMapper();

            // 5.2 Get connection output stream
            DataOutputStream wr = new DataOutputStream(conn.getOutputStream());

            // 5.3 Copy Content "JSON" into
            mapper.writeValue(wr, content);

            // 5.4 Send the request
            wr.flush();

            // 5.5 close
            wr.close();

            // 6. Get the response
            int responseCode = conn.getResponseCode();
            System.out.println("\nSending 'POST' request to URL : " + url);
            System.out.println("Response Code : " + responseCode);

            BufferedReader in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String inputLine;
            StringBuffer response = new StringBuffer();

            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();

            // 7. Print result
            System.out.println(response.toString());

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
}
  • 3.4 com/hmkcode/App.java

Here we run the server and pass the needed parameters Server API KeyRegId & data (title & message)
package com.hmkcode;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hmkcode.vo.Content;

public class App 
{
    public static void main( String[] args )
    {
        System.out.println( "Sending POST to GCM" );

        String apiKey = "AIzaSyB8azikXJKi_NjpWcVNJVO0d........";
        Content content = createContent();

        POST2GCM.post(apiKey, content);
    }

    public static Content createContent(){

        Content c = new Content();

        c.addRegId("APA91bFqnQzp0z5IpXWdth1lagGQZw1PTbdBAD13c-UQ0T76BBYVsFrY96MA4SFduBW9RzDguLaad-7l4QWluQcP6zSoX1HSUaAzQYSmI93....");
        c.createData("Test Title", "Test Message");

        return c;
    }
}

( 4 ) Run & Test

Note: If you are using an emulator to run the Android app make sure the target is Google APIs.
  • a. Register the Android app with GCM to receive Registration Id “RegId

  1. Run the Android app “built in step 2″
  2. Click Get RegId
  3. RegId will be displayed in EditText & also in LogCat
  4. Copy the RegId so we can use it in Server app.
  • b. Share “RegId” with the server

Server needs to know client(s) RegId(s) to whom the message will be sent to. Here for thesake of simplicity, our app will share RegId by manually copying it from the client and then “hard-coding” it in the server code  “copy and paste”. However, in a real world, your app needs to share its RegId with GCM server by for example “sending it using HTTP POST request”.
So just copy the RegId from LogCat window and paste it in the server App.java class
//App.java class
        c.addRegId("APA91bFqnQzp0z5IpXWdth1lagGQZw1PT..........");
  • c. Send a message from server to GCM using HTTP POST request

Now, run App.java class to send the POST request to GCM which will boradcast it to registered clients.
android-gcm-show-message
  • Client GCM Source Code @ GitHub
  • Server GCM Source Code @ GitHub

댓글 없음:

댓글 쓰기