应工作项目要求,最近在项目中集成了谷歌目前推荐的推送方式FCM。在此做一些集成过程的记录与一些注意点的分享,写的不好请轻喷。
原本谷歌原生的推送方式是GCM,也就是Google Cloud Messaging。截至2018年4月10日,Google已弃用GCM。GCM服务器和客户端API已弃用,将于2019年4月11日删除。将GCM应用程序迁移到 Firebase 云消息传递(FCM),后者继承了可靠且可扩展的GCM基础架构以及许多新功能。
简明的说就是FCM是GCM的升级版。
Firebase提供了很多服务,包含但不限于认证、通知、分析、AdMod、性能监控等。这里我们只对通知进行集成。
如果想了解原来的GCM可以在下面这个文档中进行查看:
https://developers.google.com/cloud-messaging/
事先声明下,文章中除了Demo地址是在github上不需要翻墙,其他地址都需要翻墙才可以打开。
一、先来说说为什么要是使用FCM事先消息推送功能。
做Android开发的都知道,我们最烦产品和我们说为什么我APP杀死了推送就收不到了?为什么IOS就可以?你们能做成像IOS那样吗?烦不胜烦。想抡拳头不?忍住,我们是有涵养的程序员,不用计较无知的PM的言语。
现在Android系统对后台进行的管理越来越严格,各种定制系统也对后台进程进行了各种各样的限制。基本上APP被杀死后,基本都收不到消息推送了。现在的国内第三方推送也想了各种办法去处理杀死进程后收不到推送的问题,但是效果都不是很好。
为啥IOS的系统就能那么稳定的接收消息推送呢?无论是App在前台运行时的消息接收,还是App在后台或者杀死状态下,对离线消息的接收都十分的靠谱。因为每一个苹果可以通过自家服务器维持一个长链接,每一个iOS的推送都必须和苹果打交道,所以这个过程控制得很好。
这里就要说为啥我们要用FCM了。其实Google也有自己的一套推送服务 ,就是过去的GCM,现在升级为FCM,境外的产品基本都是通过Google去实现的推送。Google提供的推送服务其实也可以做到IOS那样的效果,也能做到离线消息的稳定接收。所以如果你做得是一款海外的项目,那么你就可以选择通过FCM来实现推送,高效且稳定。国内因为墙的存在导致了我们需要去选用各种第三方的推送。在这期盼哪天Google回归大陆吧。
叽叽歪歪这么多现在开始看如何进行FCM的集成吧。
二、FCM的集成
先提供给大家集成文档的链接,以及Demo工程的github的链接。
集成文档:https://firebase.google.com/docs/cloud-messaging/
官方Demo地址:https://github.com/firebase/quickstart-android
项目中有很多的Firebase提供的服务的Demo,其中messaging文件夹下就是推送的Demo工程
1.前提条件
集成FCM是有前提条件的,也就是因为这些前提条件导致的境内项目无法正常的使用Google提供的服务。
-
Android系统必须是 Android 4.0 (Ice Cream Sandwich) 或更高版本
-
手机安装了Google Play 服务 15.0.0 或更高版本(导致境内不可使用的根本原因)
- Android SDK Manager 必须有Google Play services SDK(该条件貌似用AS开发时不是必要的,我本人就没有做到这点)。想要装的可以根据下图指示进行安装。蓝色选中部分就是。 添加Google Play services SDK.png
-
AndroidStudio 1.5以上版本,最好是用最新的版本
-
你的手机的网络是翻过墙的,不然是接收不到消息推送的
2.在控制台配置项目
与很过国内的第三方推送集成一样,需要在平台上添加你的项目,获取一些初始化需要的东西。Firebase控制台。至于注册账号什么的我就不说了,我直接说项目的创建。
登录控制台后可以看到这个页面,点击添加项目
添加项目窗口
第一步.png
创建好项目后,点击项目进入到项目的控制台页面。
选择应用类型.png
看到了这三个图标了吗?我们选择Android图标,开始对我们现在的Android工程进行关联。这里不得不说FCM还是很强大的,不仅仅支持Android的推送,同样IOS和Web的推送也支持。
关联我们的Android应用的步骤展示
照着页面的输入框进行输入,比较无脑。至于SHA-1码的获取,自己去google或者找度娘吧。
说一个注意点
当我们关联好了我们的应用后,如果对应用信息进行了修改,比如SHA-1码的修改。修改后google-services.json需要重新下载,覆盖本地的那一份。
3.添加SDK
该准备的懂准备好了,现在开添加使用的SDK,也就是配置gradle文件。
- project的gradle
buildscript {
dependencies {
// 纳入 google-services 插件
classpath 'com.google.gms:google-services:4.0.1'
}
}
allprojects {
repositories {
// Google 的 Maven 代码库
google()
}
}
- app的gradle
dependencies {
implementation 'com.google.firebase:firebase-core:16.0.3'
implementation 'com.google.firebase:firebase-messaging:17.3.1'
// 如果使用到了FirebaseInstanceIdService类,则需要加上这行
implementation 'com.google.firebase:firebase-iid:17.0.1'
// 如果需要在接收消息时配合FirebaseJobDispatcher进行耗时操作,需要加上这行
implementation 'com.google.firebase:firebase-messaging:17.3.1'
}
// 导入GMS插件,这个插件就是用来解析之前复制到app项目中的google-services.json文件的
apply plugin: 'com.google.gms.google-services'
这里要注意“apply plugin: 'com.google.gms.google-services'”要添加在gradle文件的末尾,否者会报错
Please fix the version conflict either by updating the version of the google-services plugin
(information about the latest version is available at https://bintray.com/android/android-tools/com.google.gms.google-services/)
or updating the version of com.google.android.gms to 9.0.0.
4.编写消息接收服务
- 一个继承 FirebaseMessagingService 的服务。如果您希望在后台进行除接收应用通知之外的消息处理,则必须添加此服务。要在前台应用中接收notification或者dataMessage,同样需要编写此服务。这个服务的示例代码在Demo中有。我这里简要的贴一些主要代码。
/**
* FCM 消息接收服务
* 推送分为 dataMessage(数据消息)和notification(通知消息)两种
* 区别在于:
* 1.无论应用程序位于前台还是后台,dataMessage(数据消息)都会在onMessageReceived()中处理。 数据消息是传统上与GCM一起使用的类型。
* 2.notification(通知消息)仅当应用程序位于前台时,才会在onMessageReceived()中接收。 当应用程序在后台时,将显示自动生成的通知,不会再onMessageReceived()中接收。
* 当用户点击通知时,他们将返回到应用程序。 包含通知和数据有效负载的消息将被视为通知消息。 Firebase控制台始终发送通知消息。
*/
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
/**
* @param remoteMessage 表示从Firebase Cloud Messaging收到的消息的对象,它包含了接收到的推送的所有内容
*/
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.d(TAG, "收到推送 From: " + remoteMessage.getFrom());
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "收到推送 Message data payload: " + remoteMessage.getData());
}
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "收到通知 Message Notification Body: " + remoteMessage.getNotification().getBody());
}
}
/**
* 如果更新了InstanceID令牌,则调用此方法。
* 当先前令牌的安全性受到损害,则可能更新令牌。
* 最初生成InstanceID令牌时也会调用此方法,因此您可以在此处检索令牌。
* 该回调方法可以代替Demo工程中的的MyFirebaseInstanceIDService。Demo工程中FirebaseInstanceIdService这个类也已经被废弃了。
*/
@Override
public void onNewToken(String token) {
LogUtils.dTag(TAG, "Refreshed token: " + token);
// 可以在这里将用户的FCM InstanceID令牌与应用程序维护的任何服务器端帐户关联起来。
// sendRegistrationToServer(token);
}
}
5.清单文件注册service
<!-- 一项继承 FirebaseMessagingService 的服务。如果您希望在后台进行除接收应用通知之外的消息处理,
则必须添加此服务。要接收前台应用中的通知、接收数据有效负载以及发送上行消息等,您必须继承此服务。-->
<service android:name=".push.MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
到这里为止,经过上面的一系列“sao操作”,就可以坐等接收消息了。下面我来介绍下怎么在控制台发送消息
5.获取设备注册令牌(token/RegistionID/InstanceID)
在我们的日常开发中,这个token值一般都是需要上到自己的服务端的。这样才能实现点对点的消息推送。
您的应用初次启动时,FCM SDK 会为客户端应用实例生成一个注册令牌。如果您希望定位至单台设备或创建设备组,则可以通过以下的3种方法去获取token值。
- 1.需要通过继承
FirebaseInstanceIdService
,在对调方法中来获取此令牌。不过在最新的SDK中,FirebaseInstanceIdService已经被弃用。
我这里还是贴一下示例代码
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
@Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
// sendRegistrationToServer(refreshedToken);
}
}
- 2.可以在继承FirebaseMessagingService 类的onNewToken(String token)方法中得到新得token。其实也就是消息处理服务中的一个方法。
@Override
public void onNewToken(String token) {
LogUtils.dTag(TAG, "Refreshed token: " + token);
// 可以在这里将用户的FCM InstanceID令牌与应用程序维护的任何服务器端帐户关联起来。
// sendRegistrationToServer(token);
}
- 可以在你想要获取token的地方,调用API进行token的获取
private void getPushToken() {
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
@Override
public void onComplete(@NonNull Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
Log.e(TAG, "获取token失败:", task.getException());
return;
}
// 获取新的token
String token = task.getResult().getToken();
// 将token上传给服务端
registerDevice(token);
}
});
}
注册令牌可能会在发生下列情况时更改:
应用删除实例 ID
应用在新设备上恢复
用户卸载/重新安装应用
用户清除应用数据
四、控制台发送消息
下面是在控制台发送消息的信息录入页面:
通知消息信息录入页面.png
这里有个高级选项,在高级选项中,还可以对通知标题进行设置,或者设置自定义数据。
image.png
五、关于FCM消息类型
在消息处理服务的代码实例中,类备注上写了,消息类型分为两种。
- 通知消息,有时被视为“显示消息”。此类消息由 FCM SDK 自动处理。
- 数据消息,由客户端应用处理。
想要更深入的了解消息类型的话可以查看官方文档,FCM消息类型
六、FCM使用的坑
- App 在运行的时候,推送如果有 Notification ,一般也是我们自己去控制的,所以最终它点击后的效果,我们是可以通过 PendingIntent 做部分定制的。
但是如果是在 App 没有运行的情况下,就完全归 FCM 服务帮你完成这一系列的操作,它点击后的效果,只能将你的 App 调起,并且把你需要的参数传递到你的 SplashActivity(Action 为 android.intent.action.MAIN 的 Activity) 上。
public class SplashActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (getIntent().getExtras() != null) {
for (String s : getIntent().getExtras().keySet()) {
Log.d("SplashActivity ", s + "--" + getIntent().getExtras().get(s));
// 在官网的发送notification 使用高级选项可以自定义 键值对,最终会在getIntent().getExtras()中获取到
}
Intent intent = new Intent(this, MessageActivity.class);
startActivity(intent);
}
}
}
所以我们就需要考虑两种情况下,数据的传递已经响应,这个是需要根据业务来讨论的,空聊是没有意义的。
- 如果App在后台,FCM的SDK默认会帮你自动处理消息。默认的处理形式就是帮你在系统通知栏弹出一个通知。就我写帖子时,我还没有找到如何自定义默认通知样式的方法。要是有哪位大大知道怎么自定义,麻烦你告诉我下。
网友评论