消息推送之GCM

原理

  1. 向GCM注册registerid

  2. 端发消息给GCM服务器

    这个过程需要三个参数:

    • API Key

    • registerid

    • 传递的数据

  3. GCM端将消息转发给注册的设备(通过注册的registerid)

准备工作

1、projectid

这个是项目id,通过Google API控制台页面可以申请到。具体步骤不详细说了,网上好多。

申请网址:https://code.google.com/apis/console/

2、API Key

这个是Google为新建的项目分配的API,大概类似下面这样。与上面projectid可以一块申请到。

apikey:AIzaSyAmCeG5SHgiJRqXWM4TyS2LiQhAsKHGOVA

3、安装Google服务

打开Android SDK Manager,安装Extras > Google Cloud Messaging for Android Library。

安装完之后会在你的SDK路径下的extras/google/目录下创建一个gcm文件夹,gcm文件夹下包含:

  • gcm-client
  • gcm-demo-appengine
  • gcm-demo-client
  • gcm-demo-server
  • gcm-server 子目录。

好了,到此为止,准备工作做完了,下面直接上代码。

客户端

1)、工程建好之后,首先把gcm.jar和google-play-services.jar两个jar包拷贝到libs目录下。

两个包可以从SDK下的extras/google/gcm-client目录下找到。

2)、配置AndroidManifest.xml文件

添加权限:
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

<permission android:name="工程的包名.permission.C2D_MESSAGE"

        android:protectionLevel="signature" />

<uses-permission android:name="工程的包名.permission.C2D_MESSAGE" />

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
添加服务:
<service android:name=".GCMIntentService" />
添加广播接收器:
<receiver
android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="工程的包名" />
</intent-filter>
</receiver>

3)、实现GCMIntentService类

package com.example.gcmdemo;

import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

import com.google.android.gcm.GCMBaseIntentService;

/**
 * IntentService responsible for handling GCM messages.
 */
public class GCMIntentService extends GCMBaseIntentService {

    public GCMIntentService() {
        super(CommonData.SENDER_ID);
    }

    //Get the registered broadcast from GCM
    @Override
    protected void onRegistered(Context context, String registrationId) {
        // TODO Register from the server
        Log.i("GCMIntentService", "onRegistered注册成功, registrationId=" + registrationId);
    }

    //Get the unregistered broadcast from GCM
    //Not used yet
    @Override
    protected void onUnregistered(Context context, String registrationId) {
        // TODO Unregister from the server
        Log.i("GCMIntentService", "onRegistered注册失败");
    }

    //Get the message broadcast from GCM
    @Override
    protected void onMessage(Context context, Intent intent) {
        Log.i("GCMIntentService", "接收到一条新消息:"+intent.getStringExtra("mine"));
        final String info = intent.getStringExtra("mine");
        Handler handler = new Handler(Looper.getMainLooper());
        handler.post(new Runnable() {

            @Override
            public void run() {
                Toast.makeText(getBaseContext(), "新消息:"+info, Toast.LENGTH_LONG).show();
            }
        });
    }

    @Override
    public void onError(Context context, String errorId) {
        Log.e(TAG, "Received error: " + errorId);
    }

    @Override
    protected boolean onRecoverableError(Context context, String errorId) {
        Log.e(TAG, "Received recoverable error: " + errorId);
        return super.onRecoverableError(context, errorId);
    }
}

注 :将protected void onRegistered(Context context, String registrationId)返回的registrationId发送到推送服务器

4)、实现主Activity类

package com.example.gcmdemo;

import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

import com.google.android.gcm.GCMBaseIntentService;

/**
 * IntentService responsible for handling GCM messages.
 */
public class GCMIntentService extends GCMBaseIntentService {

    public GCMIntentService() {
        super(CommonData.SENDER_ID);
    }

    //Get the registered broadcast from GCM
    @Override
    protected void onRegistered(Context context, String registrationId) {
        // TODO Register from the server
        Log.i("GCMIntentService", "onRegistered注册成功, registrationId=" + registrationId);
    }

    //Get the unregistered broadcast from GCM
    //Not used yet
    @Override
    protected void onUnregistered(Context context, String registrationId) {
        // TODO Unregister from the server
        Log.i("GCMIntentService", "onRegistered注册失败");
    }

    //Get the message broadcast from GCM
    @Override
    protected void onMessage(Context context, Intent intent) {
        Log.i("GCMIntentService", "接收到一条新消息:"+intent.getStringExtra("mine"));
        final String info = intent.getStringExtra("mine");
        Handler handler = new Handler(Looper.getMainLooper());
        handler.post(new Runnable() {

            @Override
            public void run() {
                Toast.makeText(getBaseContext(), "新消息:"+info, Toast.LENGTH_LONG).show();
            }
        });
    }

    @Override
    public void onError(Context context, String errorId) {
        Log.e(TAG, "Received error: " + errorId);
    }

    @Override
    protected boolean onRecoverableError(Context context, String errorId) {
        Log.e(TAG, "Received recoverable error: " + errorId);
        return super.onRecoverableError(context, errorId);
    }
}

注:1、SENDER_ID即为准备工作1中申请的projectid

OK,至此,客户端已经建立完成了。(^_^)

服务器

1)、工程建好之后,首先把gcm-server.jar包拷贝到lib目录下。

可以从SDK下的extras/google/gcm-demo-server目录下找到。

2)、推送服务器源码(java实现)

package com.push.server;
import java.util.ArrayList;
import java.util.List;

import com.google.android.gcm.server.Message;
import com.google.android.gcm.server.MulticastResult;
import com.google.android.gcm.server.Sender;

public class SendToGCM{
    private static final String API_KEY = "AIzaSyB8t4b-Aj_ZP1Sn9BeILUEiWwJIiyfQ5yc";

    public void send() {
        System.out.println("send to GCM start............");
        // 一对一推送
        //Result result = null;
        // 群发
        MulticastResult result = null;

        List <String> registerids = new ArrayList<String>();
        // "xxx" is registerId get from client
        registerids.add(0, "registerid");
        //send message
        try {
            Sender sender = new Sender(API_KEY);
            Message message = new Message.Builder().collapseKey("1").timeToLive(3).
                    delayWhileIdle(true).addData("title", "新消息").
                    addData("message", "this is a new message!").build();
/************************Multicast*****************************/
            result = sender.send(message, registerids, 5);

            System.out.println("result.getResults()=" + result.getResults()
                    + "\nresult.getSuccess()=" + result.getSuccess()
                    + "\nresult.getFailure()=" + result.getFailure()
                    + "\nresult.getCanonicalIds()=" + result.getCanonicalIds()
                    + "\nresult.getMulticastId()=" + result.getMulticastId()
                    + "\nresult.getTotal()=" + result.getTotal()
                    + "\nresult.getRetryMulticastIds()=" + result.getRetryMulticastIds()
                    );
            if (result.getResults() != null) {
                int canonicalRegId = result.getCanonicalIds();
                if (canonicalRegId != 0) {
                    // same device has more than on registration ID: update database
                    System.out.println("same device has more than on registration ID: update database");
                }
            }
            else {
                int error = result.getFailure();
                System.out.println("Broadcast failure: " + error);
            }
/************************Multicast*****************************/

/************************Unicast******************************/
/*
            result = sender.send(message, registerids.get(0), 5);
            System.out.println("result.getMessageId()=" + result.getMessageId() +
                    "\nresult.getCanonicalRegistrationId()" + result.getCanonicalRegistrationId() +
                    "\nresult.getErrorCodeName()" + result.getErrorCodeName());
            if (result.getMessageId() != null) {
               String canonicalRegId = result.getCanonicalRegistrationId();
               if (canonicalRegId != null) {
               // same device has more than on registration ID: update database
               }
            }
            else {
               String error = result.getErrorCodeName();
               if (error.equals(Constants.ERROR_NOT_REGISTERED)) {
               // application has been removed from device - unregister database
               }
            }
*/
/************************Unicast******************************/
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("send to GCM end............");
    }
}

注:

  • API_KEY 即为准备工作2中申请到的apikey

  • registerid即为主Activity中GCMRegistrar.getRegistrationId(mContext); 的返回值

OK,至此,服务端的构筑也已经完成了。

特点

下面总结一下GCM的优缺点,不全,欢迎补足指正

优点:

  1. Google提供的服务,原生,简单,不需实现部署服务器端

  2. 提供群发和单一推送功能

  3. 提供延时发送功能。如果当前手机不在线,Google会把这个消息入队并存储这个消息,当用户在线时再进行发送。存活时间在发送端进行指定。

  4. 应用程序不需要提前运行来接收这个消息。在手机端,系统使用适当的permission通过Intent broadcast把这个消息broadcast到特定的程序,然后特定的程序获得这个消息。这样就唤醒了这个程序。

缺点:

  1. android版本的限制(android2.2以上)

  2. 该服务在国内不够稳定,尤其现在,Google被封,所以要想在国内使用建议还是放弃

  3. 不能保证消息的发送和消息的发送顺序

  4. 它不提供任何的用户界面或者其他的东西来处理消息。C2DM只是简单的把收到的原始消息传递给程序。这个程序提供了处理这个消息的方法

作者:dykingdy
出处:https://lidaguang1989.github.io/
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。