FCM实现消息推送

  • Post author:
  • Post category:其他



目录


1.FCM的开发初衷


2.设计思路


3.FCM官方API开发


1.FCM的开发初衷

最近刚刚毕业,在公司接到的第一个正式任务是要求我将消息推送到firebase服务器上从而实现消息推送到安卓客户端。由于该产品面向海外用户,所以需要通过谷歌提供的服务器进行消息推送。至于fcm的概念,大家可以自行百度,我不再重复。直接讲述整个代码的coding过程,代码我是已经跑通了的,可以根据我的文档进行参数设置就可以进行简单的消息推送测试了。当然,如果在了解我的代码后,也可以进行代码的重写,我提供的只是一个很简单的demo。

2.设计思路

在解释我的代码前,作为开发者,你需要向安卓端的开发获取一个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开发了。

3.FCM官方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<String> 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<SendResponse> getResponses();

这个List<SendResponse>是对应每一条消息的发送情况,这个队列对应的位置就是和发送的顺序是一样的,可以根据此统计哪些消息发送成功或者发送失败。

代码在我的github地址,如果有需要可以下载,直接部署即可。


https://github.com/SmilingCobra/push-fcm



版权声明:本文为qq1641530151原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。