先,翻不了墙一切都白搭……
参考google官方指导文档,在google中搜索GCM,或者直接点击此处打开。本文均以Android为例,打开页面后,点击左上方的”TRY IT ON ANDROID”按钮,进入针对安卓的指导页。以下步骤官方指导写的比较详细的,本文就不赘述,一笔带过,有需要注意的会补充。
下载Android示例工程
下载下来是个压缩包,GCM的工程目录为google-services-master/android/gcm,简单介绍下各个类的作用:
<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());
下载配置文件。
下载过程中会给出API Key和Sender ID。(没记住也没关系,管理台上也可以查到,分别对应API Key和Project number)
将配置文件放入工程
注意,工程下载好后,里面默认代码使用的API Key以及Sender ID需要修改。
API Key: GcmSender.java(模拟服务器向Google发送推送消息使用,后面可以不用main函数,直接用浏览器插件发消息的方式,更方便点。)
Sender ID:RegistrationIntentService.java(代码默认使用的是”R.string.gcm_defaultSenderId”)。
安装并运行示例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。
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!",
}
}
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!",
}
}
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!",
}
}
Gradle编译不过,提示无法下载play-services-gcm:8.4.0。
可能jcenter还没有收录这个版本(也可能是网络问题),尝试使用早一点的版本,例如build.gradle文件中改为8.3.0就好了。顺便提醒下别忘了在SDK Manager中,安装Extras下面的Google Play services,这个组件默认是不安装的。
使用命令行调用main函数的方式发送消息,连接超时或失败。
没用到proxy,使用postman等工具发送。
app运行过程中,日志打印Notification.xxx方法找不到。
升级android sdk到最新版本,官网下载的代码依赖的sdk是比较新的。
发给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]
MyGcmListenerService.java中,弹出状态栏消息时会设置一个notification ID,不同ID会弹出不同的通知,同一个ID会一直刷新同一个通知。
设置固定的ID就可以做到”收到xx条新消息”这种提示啦。
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
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;