GCM Google官方示例的简单介绍和使用

GCM Google官方示例的简单介绍和使用

准备工作

  先,翻不了墙一切都白搭……

Google账号

  • 申请Google账号
  • 进入Google开发管理台
  • 创建工程(Google管理台上的Project)
  • 开启Google Cloud Messaging API。

Demo工程

  参考google官方指导文档,在google中搜索GCM,或者直接点击此处打开。本文均以Android为例,打开页面后,点击左上方的”TRY IT ON ANDROID”按钮,进入针对安卓的指导页。以下步骤官方指导写的比较详细的,本文就不赘述,一笔带过,有需要注意的会补充。

  1. 下载Android示例工程
      下载下来是个压缩包,GCM的工程目录为google-services-master/android/gcm,简单介绍下各个类的作用:

    • MyGcmListenerService.java
      GCM接收监听服务类,接收GCM发过来的通知消息,并显示到手机状态栏中。
      对应的AndroidManifest.xml配置如下:
      
      <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="gcm.play.android.samples.com.gcmquickstart"/>
        intent-filter>
      receiver>
      
      
      <service
        android:name=".MyGcmListenerService"
        android:exported="false">
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
        intent-filter>
      service>
      

    GcmReceiver接收,然后发送给GcmListenerService,我们在GcmListenerServcie的实现类中处理收到的gcm消息。Google官方对GcmReceiver的解释(原文链接):

    WakefulBroadcastReceiver that receives GCM messages and delivers them to an application-specific GcmListenerService subclass.

    如何将通知消息展示到状态栏:

    Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.ic_stat_ic_notification)
                .setContentTitle("GCM Message")
                .setContentText(message + count)
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);
    
    NotificationManager notificationManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    
    int notificationId = Integer.parseInt(("" + System.currentTimeMillis()).substring(10));
    notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
    • RegistrationIntentService.java
      向GCM服务器进行设备注册,获取注册token,该token会作为通知消息接收设备的标识,该类中还进行了topics的订购。
    • MyInstanceIDListenerService.java
      接收token更新通知,收到通知后会重新通过RegistrationIntentService获取新的token。
    • GcmSender.java
      推送消息发送类,通过该类向GCM发送HTTP消息,GCM再推送给终端设备。该类并不是必需的,下文也提到了,可以通过Postman等工具直接发送HTTP消息。

  2. 下载配置文件。
      下载过程中会给出API Key和Sender ID。(没记住也没关系,管理台上也可以查到,分别对应API Key和Project number)

  3. 将配置文件放入工程

    注意,工程下载好后,里面默认代码使用的API Key以及Sender ID需要修改。

    API Key: GcmSender.java(模拟服务器向Google发送推送消息使用,后面可以不用main函数,直接用浏览器插件发消息的方式,更方便点。)

    Sender ID:RegistrationIntentService.java(代码默认使用的是”R.string.gcm_defaultSenderId”)。

  4. 安装并运行示例App
      工程打开时会向Google注册,并获取注册Token,logcat日志中会打出来。后面发送消息时以获取到的token作为客户端设备标识。

02-23 10:39:18.709 19735-19763/gcm.play.android.samples.com.gcmquickstart I/RegIntentService: GCM Registration Token:
 dnbhEeyYCWg:APA91bH_yYRmgPsuzpC7qMKp86JV3jR5d...Iw6VvPHilRa2d9u7sW4Xs6El2S1nsqtGM4yO2vVjHv-nSs_DkF3-sdn3b...7mxrbdsyl5xb53


发送通知消息

  下载下来的工程中GcmSender.java这个类就是专门发消息的,Google的指导中有给出调用其Main函数的命令,本文推荐直接通过浏览器插件或工具发送(反正就是个HTTPS的消息,用什么发都一样,对吧)。
  推荐”Postman”,通过Chrome网上应用店安装,有独立的应用版本。

消息内容如下:
其中消息头中的key为API Key(Google管理台上可以查),token为上文第四步中提到的注册token。

  • 消息1:发给某一个topic,to为topic的名称(可以实现群发)。
    参考链接:https://developers.google.com/cloud-messaging/topic-messaging
https://gcm-http.googleapis.com/gcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA
{
  "to": "/topics/foo-bar",
  "data": {
    "message": "This is a GCM Topic Message!",
   }
}
  • 消息2:指定发送给一个设备,to即上文提到的注册token。
https://gcm-http.googleapis.com/gcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA
{
  "to": "token",
  "data": {
    "message": "This is a GCM token Message!",
   }
}
  • 消息3:发送给多个设备,token1、token2即上文提到的注册token。
https://gcm-http.googleapis.com/gcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA
{
  "registration_ids": ["token1","token2"],
  "data": {
    "message": "This is a GCM token Message!",
   }
}


可能遇到的问题

  1. Gradle编译不过,提示无法下载play-services-gcm:8.4.0。
    可能jcenter还没有收录这个版本(也可能是网络问题),尝试使用早一点的版本,例如build.gradle文件中改为8.3.0就好了。顺便提醒下别忘了在SDK Manager中,安装Extras下面的Google Play services,这个组件默认是不安装的。

  2. 使用命令行调用main函数的方式发送消息,连接超时或失败。
    没用到proxy,使用postman等工具发送。

  3. app运行过程中,日志打印Notification.xxx方法找不到。
    升级android sdk到最新版本,官网下载的代码依赖的sdk是比较新的。

  4. 发给topics的消息终端无法收到
    Google给的消息示例中to的值为/topics/foo-bar,实际测试时发现是收不到这个推送消息的,查看RegistrationIntentService.java代码发现针对topics的消息,需要在获取到token后,订购相应的topics(默认只订购了/topics/global,所以发向/topics/foo-bar的收不到)。
    相关代码:

/**
 * Subscribe to any GCM topics of interest, as defined by the TOPICS constant.
 *
 * @param token GCM token
 * @throws IOException if unable to reach the GCM PubSub service
 */
// [START subscribe_topics]
private void subscribeTopics(String token) throws IOException {
    GcmPubSub pubSub = GcmPubSub.getInstance(this);
    for (String topic : TOPICS) {
        pubSub.subscribe(token, "/topics/" + topic, null);
    }
}
// [END subscribe_topics]


几个常用参数

1. 在收到多条推送消息时,状态栏如何控制弹多条还是保持一条。

MyGcmListenerService.java中,弹出状态栏消息时会设置一个notification ID,不同ID会弹出不同的通知,同一个ID会一直刷新同一个通知。
设置固定的ID就可以做到”收到xx条新消息”这种提示啦。

notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());

2. App最小化或Activity销毁后,点击状态栏通知要打开指定的Activity

MyGcmListenerService.java中,”PendingIntent.FLAG_ONE_SHOT”改为”PendingIntent.FLAG_UPDATE_CURRENT”(或者”0”?)即可,具体每个flag的含义参考其注释。

Intent intent = new Intent(this, Main2Activity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */
  , intent, PendingIntent.FLAG_ONE_SHOT);
/**
 * Flag indicating that this PendingIntent can be used only once.
 * For use with {@link #getActivity}, {@link #getBroadcast}, and
 * {@link #getService}. 

If set, after * {@link #send()} is called on it, it will be automatically * canceled for you and any future attempt to send through it will fail. */ public static final int FLAG_ONE_SHOT = 1<<30; /** * Flag indicating that if the described PendingIntent does not * already exist, then simply return null instead of creating it. * For use with {@link #getActivity}, {@link #getBroadcast}, and * {@link #getService}. */ public static final int FLAG_NO_CREATE = 1<<29; /** * Flag indicating that if the described PendingIntent already exists, * the current one should be canceled before generating a new one. * For use with {@link #getActivity}, {@link #getBroadcast}, and * {@link #getService}.

You can use * this to retrieve a new PendingIntent when you are only changing the * extra data in the Intent; by canceling the previous pending intent, * this ensures that only entities given the new data will be able to * launch it. If this assurance is not an issue, consider * {@link #FLAG_UPDATE_CURRENT}. */ public static final int FLAG_CANCEL_CURRENT = 1<<28; /** * Flag indicating that if the described PendingIntent already exists, * then keep it but replace its extra data with what is in this new * Intent. For use with {@link #getActivity}, {@link #getBroadcast}, and * {@link #getService}.

This can be used if you are creating intents where only the * extras change, and don't care that any entities that received your * previous PendingIntent will be able to launch it with your new * extras even if they are not explicitly given to it. */ public static final int FLAG_UPDATE_CURRENT = 1<<27; /** * Flag indicating that the created PendingIntent should be immutable. * This means that the additional intent argument passed to the send * methods to fill in unpopulated properties of this intent will be * ignored. */ public static final int FLAG_IMMUTABLE = 1<<26;

你可能感兴趣的:(源码分析)