Firebase Cloud Messaging (FCM) - Push Notification (Java)

Asked

Viewed 2,495 times

1

I am working on a backend that I would like to use to send / Push notifications to android app using the Firebase Cloud Messaging (FCM).

I’ve read the documentation a few times (WCF) but I still have many doubts.

I know it is possible to use the Admin SDK or some other server protocol, but what I decided was to use the Admin SDK.

I have already registered in the Firebase Console, generated the server key and other necessary keys.

Below is an example of a message:

POST https://fcm.googleapis.com/v1/projects/nome_projeto/messages:send HTTP/1.1
Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA

{
 "message":{
   "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
   "notification":{
     "body" : "Primeira Mensagem FCM!",
     "title" : "Mensagem FCM"
  }
}

Follow part of my source:

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


private Resource resource = new ClassPathResource("./service-account.json");
private InputStream resourceInputStream;

public String obterAccessToken() throws IOException {
    resourceInputStream = resource.getInputStream();
    GoogleCredential googleCredential = GoogleCredential
        .fromStream(resourceInputStream)
        .createScoped(Collections.singletonList("https://www.googleapis.com/auth/firebase.messaging"));
    googleCredential.refreshToken();
    return googleCredential.getAccessToken();
}


public void iniciarFirebase() throws IOException {
    resourceInputStream = resource.getInputStream();
    FirebaseOptions options = new FirebaseOptions.Builder()
        .setCredentials(GoogleCredentials.fromStream(resourceInputStream))
        .build();
        FirebaseApp.initializeApp(options);
}

What now? How do I send the message to an App?

I need to generate a Json with the information of these two methods?

  • 1

    firebase Adminsdk does not support sending notifications even under the documentation: https://firebase.google.com/docs/admin/setup

  • 1

    It is possible to send the notification with an HTTP call on the FCM server: https://firebase.google.com/docs/cloud-messaging/http-server-ref

  • Very useful both tips. I’m also trying to make work the example you posted. I never used and I’m trying to learn Retrofit2 (Call).

  • 1

    It’s very easy, I did it in a project where the banckend had to notify users, and in the backend and easier still to use retrofit, because it does not need to make call asincrona, you can run the direct call that the main thread supports, in case you do not want to treat everything as Entity, vc can use Okhttp lib to handle everything directly from the network layer

  • In the example, sendPush(Map<String, Object> param) is not accepting the return of Return Response.body(). I still don’t understand pq...

  • 1

    In vdd, the syntax is incorrect, because the return of the method is VOID and does not accept Return, because I did many sending methods and some of them I did asynchronous execution, because I did not need to know the result

  • 1

    I edited and switched to the correct syntax

  • If I use it as it is now (public Firebasepushresponse sendPush(Map<String, Object> param) ) it still continues with the following error : The Return type is incompatible with Firebasecall.sendPush(Map<String,Object>). I’m trying to make a simpler example here to try to understand.

  • 1

    I omitted the converter, because you can choose the lib of your preference, here in this example it shows how to add a converter https://futurestud.io/tutorials/retrofit-2-adding-customizing-the-gson-converter

  • 1

    The Converter is for you to treat the answers, as a REST framework vc treats the answers directly as an entity and not as Json, but for that you need to map the answers with a JSON library, in the case of the example you use Gson (google library)

  • It’s about to get... Now the following error happens: Exception in thread com.fasterxml.Jackson.databind.exc.Invaliddefinitionexception: Cannot Construct instance of instance of br.com.teste.FirebasePushResponse (no Creators, like default Construct, exist): cannot deserialize from Object value (no delegate- or Property-based Creator) at [Source: (okhttp3.Responsebody$Bomawarereader); line: 1, column: 2]... Thank you

  • 1

    That’s why I like to use GSON, it doesn’t need many treatments, but tbm is limited to how to map, but you can try this solution to your error with Jackson: http://www.baeldung.com/jackson-exception

  • I switched to GSON friend... It was no mistake. Firebasepush: sucess. Ufaaaaaaa. Now to test if you are sending the push correctly I need to install an app on a mobile phone? Can you get any I can test? Or do I need to "do" one?

  • Rlx, some topics may take days to be synchronized Android/ IOS, but if you send the call to the topic default, it will work for sure, but it is very easy to push notification on android, already have some templates ready in the own android studio

Show 9 more comments

2 answers

3


Using the library Retrofit2 to submit Request in java, I have implemented the following classes and methods

class NotificationService {

    private FirebaseCall firebaseCall;

    public NotificationService() {

        Retrofit r = new Retrofit.Builder().baseUrl("https://fcm.googleapis.com/").build();

        firebaseCall = r.create(FirebaseCall.class);

    }

    /**
     * Envia notificação para um dispositivo unico
     * 
     * @param mensagem
     * @param action
     * @param device
     */
    public void sendNotificationDevice(String mensagem, String action, String device) {

        Map<String, String> data = new HashMap<>();
        data.put("message", mensagem);
        data.put("action", action);
        sendPush(getDataDevice(device, data), this);

    }

    /**
     * Envia notificação para um canal
     * 
     * @param channel
     * @param mensagem
     * @param title
     * @param action
     */
    public void sendNotification(String channel, String mensagem, String title, String action) {

        Map<String, String> data = new HashMap<>();
        data.put("message", mensagem);
        data.put("title", title);
        data.put("action", action);
        sendPush(getDataTopic(channel, data), this);
    }

    /**
     * Aqui é feita a chamada da requisição
     */
    public FirebasePushResponse sendPush(Map<String, ?> param, Callback<FirebasePushResponse> callback) {
        Response<FirebasePushResponse> response = null;
        try {
            response = firebaseCall.sendPush(param).execute();
            if (response.isSuccessful()) {
                logger.info("FirebasePush: sucess");
            } else {
                logger.error("FirebasePush: erro");
                logger.error("Code: " + response.code() + "\n\rmessege: " + response.message());
            }

        } catch (IOException e) {
            showErro(e);
        }

        return response.body();

    }



    /**
     * Map para notificar um dispositivo unico
     * 
     * @param to
     *            codigo do FirebaseInstanceId do dispositivo data channel
     * @param data
     * @return map
     */
    public Map<String, ?> getDataDevice(String to, Map<String, String> data) {

        Map<String, Object> map = new HashMap<>();
        String toRaw = to;
        map.put("to", toRaw);
        map.put("data", data);

        return map;
    }

    /**
     * envia notificação para um TOPIC
     * 
     * @param to
     *            data channel
     * @param data
     * @return map
     */
    public Map<String, ?> getDataTopic(String to, Map<String, ? extends Object> data) {

        Map<String, Object> map = new HashMap<>();
        String toRaw = "/topics/" + to;
        map.put("to", toRaw);
        map.put("data", data);

        return map;
    }



    public FirebasePushResponse sendPush(Map<String, Object> param) {
        Response<FirebasePushResponse> response = null;
        try {
            response = firebaseCall.sendPush(param).execute();
            if (response.isSuccessful()) {
                logger.info("FirebasePush: sucess");
            } else {
                logger.error("FirebasePush: erro");
                logger.error("Code: " + response.code() + "\n\rmessege: " + response.message());
            }

        } catch (IOException e) {
            showErro(e);
        }

        return response.body();
    }
}

interface FirebaseCall {
    static final String PATH = "fcm/send";

    @POST(PATH)
    @Headers({ "Authorization:key=" + INSIRA_SUA_CHAVE_DO_FIREBASE_AQUI, "Content-Type:application/json" })
    public Call<FirebasePushResponse> sendPush(@Body Object object);

    @POST(PATH)
    @Headers({ "Authorization:key=" + INSIRA_SUA_CHAVE_DO_FIREBASE_AQUI, "Content-Type:application/json" })
    public Call<FirebasePushResponse> sendPush(@Body Map<String, Object> object);
}

class FirebasePushResponse {

    private String messageId, error;

}

And to use just call the class

NotificationService notification = new NotificationService();
FirebasePushResponse resposta = notification.sendNotification("default","Hello World","Hellow Title","br.com.action.sync");

To receive notification for a topic you must have the devices synchronized in the topic

FirebaseMessaging.getInstance().subscribeToTopic("default");

2

Hello,

It’s a shame that this post is in Portuguese because the documentation on the FCM (Firebase Cloud Messaging) is completely scarce around the world and in English could help more people, but let’s the answers.

The example below is completely didactic and should be suitable for a production environment.

1 - You must access Firebase and reach the "Firebase Admin SDK", there you must click on "Generate new private key" (if you have not yet generated a).

2 - This key is a json with the structure below and should be saved in a file:

{
  "type": "service_account",
  "project_id": "xxxxxxx",
  "private_key_id": "xxxxxxx",
  "private_key": "-----BEGIN PRIVATE KEY-----xxxxxxx -----END PRIVATE KEY-----\n",
  "client_email": "[email protected]",
  "client_id": "xxxxxxx",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/xxxxxxx.iam.gserviceaccount.com"
}

3 - You must import the package below in your project.

    <dependency>
        <groupId>com.google.firebase</groupId>
        <artifactId>firebase-admin</artifactId>
        <version>6.3.0</version>
        <scope>compile</scope>
    </dependency>

4 - Below is the example source code

public class FCMService  {

    public void send(String idPush, String msg)  {

        try {

            GoogleCredentials fromStream = GoogleCredentials.fromStream(new FileInputStream(new File("/path para o json gerado no passo 2")));

            FirebaseOptions firebaseOptions = new FirebaseOptions.Builder().setConnectTimeout(10000).setCredentials(fromStream).build();

            FirebaseApp firebaseApp = FirebaseApp.initializeApp(firebaseOptions);

            FirebaseMessaging firebaseMessaging = FirebaseMessaging.getInstance(firebaseApp);

            Message message = Message.builder().putData("msg", msg).setToken(idPush).build();

            String response = firebaseMessaging.send(message);

            System.out.println(response);

        } catch (FirebaseMessagingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

PS: Remember that Google is constantly changing the FCM which has left everyone lost. The above solution already uses the new FCM endpoint (https://fcm.googleapis.com/v1/{Parent=Projects/*}/messages:send) and no longer the legacy endpoint that many libraries are still pointing to (https://fcm.googleapis.com/fcm/send).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.