uniquejava / blog

My notes regarding the vibrating frontend :boom and the plain old java :rofl.
Creative Commons Zero v1.0 Universal
11 stars 5 forks source link

Spring Boot + Flutter Push Notification #304

Open uniquejava opened 4 years ago

uniquejava commented 4 years ago

有三个topic要谈:

  1. sending push notification to a topic
  2. directly to the users’ device
  3. sending messages with additional data payload.

消息格式: https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages

pom.xml

<dependency>
    <groupId>com.google.firebase</groupId>
    <artifactId>firebase-admin</artifactId>
    <version>6.13.0</version>
</dependency>

最新版本请参见: https://firebase.google.com/docs/admin/setup#add-sdk

Authenticate a service account and authorize it to access Firebase services

you must generate a private key file in JSON format.

To generate a private key file for your service account:

In the Firebase console, open Settings > Service Accounts.

Click Generate New Private Key, then confirm by clicking Generate Key.

Securely store the JSON file containing the key.

使用以上json文件初始化FCM Admin SDK


FileInputStream serviceAccount =
  new FileInputStream("path/to/serviceAccountKey.json");

FirebaseOptions options = new FirebaseOptions.Builder()
  .setCredentials(GoogleCredentials.fromStream(serviceAccount))
  .setDatabaseUrl("https://<DATABASE_NAME>.firebaseio.com")
  .build();

FirebaseApp.initializeApp(options);

如果是通过设置环境变量 GOOGLE_APPLICATION_CREDENTIALS

指定json文件的位置, 则以上代码修改为:

FirebaseOptions options = new FirebaseOptions.Builder()
    .setCredentials(GoogleCredentials.getApplicationDefault())
    .setDatabaseUrl("https://<DATABASE_NAME>.firebaseio.com/")
    .build();

FirebaseApp.initializeApp(options);

例子: export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/service-account-file.json"

或者使用以上json文件得到一个short-lived JWT token

private static String getAccessToken() throws IOException {
  GoogleCredential googleCredential = GoogleCredential
      .fromStream(new FileInputStream("service-account.json"))
      .createScoped(Arrays.asList(SCOPES));
  googleCredential.refreshToken();
  return googleCredential.getAccessToken();
}

如果要使用FCM service, SCOPES的值必须为 https://www.googleapis.com/auth/firebase.messaging

使用JWT Token

headers: {
  'Authorization': 'Bearer ' + accessToken
}

以上整理自: https://firebase.google.com/docs/cloud-messaging/auth-server

搭建FCM Server的要求

https://firebase.google.com/docs/cloud-messaging/server#role

搭建FCM Server的两种方式

  1. FCM HTTP v1 API
  2. Firebase Admin SDK for FCM (封装了FCM HTTP v1 API)

发送消息 (cyper实战)

public interface FcmService {

    /**
     * 发送消息到指定设备.
     * 
     * @param deviceToken 设备Token
     * @param title       标题
     * @param body        内容
     * @param payload     其他额外参数
     * @throws FirebaseMessagingException
     */
    void sendTokenMessage(String deviceToken, String title, String body, Map<String, String> payload)
            throws FirebaseMessagingException;

    /**
     * 发送topic.
     * 
     * @param topic   主题
     * @param title   标题
     * @param body    内容
     * @param payload 其他额外参数
     * @throws FirebaseMessagingException
     */
    void sendTopicMessage(String topic, String title, String body, Map<String, String> payload)
            throws FirebaseMessagingException;

    /**
     * 群发消息到多个设备
     * 
     * @param deviceTokens 多个设备的Token
     * @param title        标题
     * @param body         内容
     * @param payload      其他额外参数
     * @throws FirebaseMessagingException
     */
    void sendMessage2MultiDevices(List<String> deviceTokens, String title, String body, Map<String, String> payload)
            throws FirebaseMessagingException;

}

@Service
@Slf4j
public class FcmServiceImpl implements FcmService {
    @Override
    public void sendTokenMessage(String deviceToken, String title, String body, Map<String, String> payload)
            throws FirebaseMessagingException {
        sendMessage(deviceToken, title, body, payload, false);
    }

    @Override
    public void sendTopicMessage(String topic, String title, String body, Map<String, String> payload)
            throws FirebaseMessagingException {

        sendMessage(topic, title, body, payload, true);
    }

    private void sendMessage(String topicOrToken, String title, String body, Map<String, String> payload,
            boolean isTopic) throws FirebaseMessagingException {
        // See documentation on defining a message payload.
        Builder builder = Notification.builder();
        builder.setTitle(title);
        builder.setBody(body);
        Notification notification = builder.build();

        Message.Builder messageBuilder = Message.builder().setNotification(notification);

        if (payload != null) {
            messageBuilder.putAllData(payload);
        }

        if (isTopic) {
            messageBuilder.setTopic(topicOrToken);
        } else {
            messageBuilder.setToken(topicOrToken);
        }

        // Send a message to the device corresponding to the provided registration
        // token.

        String response = FirebaseMessaging.getInstance().send(messageBuilder.build());

        if (log.isInfoEnabled()) {
            log.info(response);
        }
    }

    @Override
    public void sendMessage2MultiDevices(List<String> deviceTokens, String title, String body,
            Map<String, String> payload) throws FirebaseMessagingException {
        Builder builder = Notification.builder();
        builder.setTitle(title);
        builder.setBody(body);
        Notification notification = builder.build();

        // @formatter:off

        MulticastMessage.Builder messageBuilder = MulticastMessage.builder()
                .setNotification(notification)
                .addAllTokens(deviceTokens);

        if(payload != null) {
            messageBuilder.putAllData(payload);
        }

        // @formatter:on

        BatchResponse response = FirebaseMessaging.getInstance().sendMulticast(messageBuilder.build());

        if (log.isInfoEnabled()) {
            log.info(response.getSuccessCount() + " messages were sent successfully");
        }

    }

}

App端 用到的 Dart package (pub spec)

Pub Spec: firebase_messaging 6.0.13

References

Spring Boot: Send push notifications from Spring Boot server-side application using FCM

新鲜出炉的:Flutter Push Notifications using flutter firebase messaging with example

Concise: FCM Push Notifications for Flutter

FilledStacks: Push Notifications in Flutter using Firebase

uniquejava commented 4 years ago

测试(device token || topic )

通过REST API在线发: https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send (topic 和 token都行!)

image

通过firebase console发 (topic 和 token和user segment!)

image