目录
1.FCM的开发初衷
2.设计思路
3.FCM官方API开发
最近刚刚毕业,在公司接到的第一个正式任务是要求我将消息推送到firebase服务器上从而实现消息推送到安卓客户端。由于该产品面向海外用户,所以需要通过谷歌提供的服务器进行消息推送。至于fcm的概念,大家可以自行百度,我不再重复。直接讲述整个代码的coding过程,代码我是已经跑通了的,可以根据我的文档进行参数设置就可以进行简单的消息推送测试了。当然,如果在了解我的代码后,也可以进行代码的重写,我提供的只是一个很简单的demo。
在解释我的代码前,作为开发者,你需要向安卓端的开发获取一个json格式文件,这个文件是他们在谷歌的官网进行注册时从控制台上进行下载的文件。文件的名字是和安卓注册的app名称有关系的,大概的格式内容如下
{
"type": "service_account",
"project_id": ,
"private_key_id":,
"private_key": ,
"client_email": ,
"client_id": ,
"auth_uri": ,
"token_uri": ,
"auth_provider_x509_cert_url": ,
"client_x509_cert_url":
}
这个json格式的文件作为开发者而言是需要将它放置在某个目录下的,因为google提供的原生api要获取这个文件的输入流来解析这个文件,具体过程我会在之后讲解到。
除此之外,安卓客户端还需要提供给客户端一个packageName,这个包名同样也是在google提供的控制台中可以查找到的以及安卓需要提供一个有效channelId,这个channelId在安卓8以上是需要提供给谷歌服务器进行通道识别,从而实现消息推送的。
以上三个部分都准备完毕后,就可以进行进一步的api开发了。
官方api的github地址https://github.com/firebase/firebase-admin-java
这个代码里面提供了很多种发送请求的方式。在该项目中,我只用到了其中的一种,向多个用户群发消息。
在开发过程中,我们可以通过业务逻辑编写,对一条消息需要发送的用户进行筛选,之后向这些用户进行消息群发操作。
现在来看一下整个程序的主流程。
/**
*
* @param appId 需要开发的appId 这个id和发送消息没有任何关系,只是作为key为了查找app对应的FirebaseApp信息使用
* @param title 你要推送的消息标题
* @param body 消息内容
* @param packageName 这个包名 是ios/android在fcm官网注册获取到的包名
* @param clickAction 这个是点击消息后的触发事件 ,如果想要触发app可以默认设置为OPEN_STOCK_ACTIVITY
* @param ttl 消息过期时间 单位在api要求为ms
* @param channelId 设置发送的频道的id
* @param tokens 用户设备唯一标识列表,其中包含若干个用户设备,官网限制为100个
* @return
* @throws IOException
*/
public static BatchResponse push(String appId, String title, String body, String packageName, String clickAction, Long ttl, String channelId, List tokens) throws IOException {
BatchResponse batchResponse = null;
if(!isInit(appId))
initSDK(appId);
FirebaseApp firebaseApp = Constants.FIREBASE_APP_MAP.get(appId);
try {
if(firebaseApp !=null){
//获取AndroidConfig.Builder对象
AndroidConfig.Builder androidConfigBuilder=AndroidConfig.builder();
//获取AndroidNotification.Builder对象
AndroidNotification.Builder androidNotifiBuilder=AndroidNotification.builder();
//可以存放一个数据信息进行发送,使得app开发客户端可以接受信息
androidConfigBuilder.putData("test","this is a test data");
androidConfigBuilder.setRestrictedPackageName(packageName);//设置包名
androidConfigBuilder.setTtl(ttl);//设置过期时间 官方文档以毫秒为单位
androidNotifiBuilder.setTitle(title);// 设置消息标题
androidNotifiBuilder.setBody(body);// 设置消息内容
androidNotifiBuilder.setClickAction(clickAction); //设置触发事件
androidNotifiBuilder.setChannelId(channelId);
AndroidNotification androidNotification=androidNotifiBuilder.build();
androidConfigBuilder.setNotification(androidNotification);
AndroidConfig androidConfig=androidConfigBuilder.build();
//在进行消息发送之前要设置代理 这个非常重要,因为访问谷歌的服务器需要通过代理服务器在进行访问
initProxy("yourHost",80,"yourUsername","yourPassword");
//构建消息
MulticastMessage message = MulticastMessage.builder()
.addAllTokens(tokens) //向所有的设备推送消息
.setAndroidConfig(androidConfig)
.build();
batchResponse = FirebaseMessaging.getInstance(firebaseApp).sendMulticast(message);
}
} catch (Exception e) {
e.printStackTrace();
}
return batchResponse;
}
根据代码进行分析,首先appId和发送消息业务没有关系。只是因为可能会有多个app调用fcm服务,所以采用一个map来保存每个 app对应的FirebaseApp信息。至于如何初始化每个FirebaseApp信息,方法如下所示
private static void initSDK(String appId) throws IOException {
String jsonPath = ""; //存放加密信息的部分
if(jsonPath!=null) {
FileInputStream serviceAccount = new FileInputStream(jsonPath);
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.build();
//初始化firebaseApp
FirebaseApp firebaseApp = null;
try {
firebaseApp = FirebaseApp.initializeApp(options);
} catch (Exception e) {
firebaseApp = FirebaseApp.getInstance(appId);
if(firebaseApp!=null){
firebaseApp.delete();
firebaseApp = FirebaseApp.initializeApp(options);
}
}
//存放
Constants.FIREBASE_APP_MAP.put(appId, firebaseApp);
}
}
可以看到,我们需要根据这个appId找到对应的json格式数据的路径,这个匹配的方法需要由开发者的业务来决定。
至于这个部分
try {
firebaseApp = FirebaseApp.initializeApp(options);
} catch (Exception e) {
firebaseApp = FirebaseApp.getInstance(appId);
if(firebaseApp!=null){
firebaseApp.delete();
firebaseApp = FirebaseApp.initializeApp(options);
}
}
如果我们的证书信息,也就是内个json格式的文件需要进行更新时,同样需要替换firebaseApp信息。但是如果已经初始化后firebaseApp信息后再次调用FirebaseApp.initializeApp(options)会直接报错,所以需要先将其进行delete。
初始化firebaseApp完成后,就是设置androidConfigBuilder这个类的时候,其中入参的意义我已经在注释中说明清楚,不再赘述。putData方法则是向服务器发送数据格式的消息,安卓端将会获取到你发送的这个消息,至于这个消息的key和value需要开发者和安卓端进行协商。
在发送之前,一定需要进行参数设置,让请求走代理方式,因为谷歌的服务器在国内都是被禁的。代理设置的方法如下,网上找了很多,下面的是靠谱的。
/**
*
* @param host ip地址
* @param port 端口号
* @param username 如果有则填
* @param password 如果有则填
*/
private static void initProxy(String host, int port, final String username,final String password) {
System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
//HTTP代理
System.setProperty("http.proxyHost",host);
System.setProperty("http.proxyPort",Integer.toString(port));
//HTTPS代理
System.setProperty("https.proxyHost",host);
System.setProperty("https.proxyPort",Integer.toString(port));
if(username != null && password !=null)
Authenticator.setDefault(new BasicAuthenticator(username,password));
}
之后就是消息发送,消息发送过程很简单,有google底层实现的http请求完成。在消息发送完成后,开发者将会获取一个BatchResponse对象。这个就是谷歌提供给开发者的回执。
List getResponses();
这个List
代码在我的github地址,如果有需要可以下载,直接部署即可。
https://github.com/SmilingCobra/push-fcm