J'ai essayé d'implémenter la notification push Firebase en Java

Chose que tu veux faire

régulation

Introduction merci

Honnêtement, il n'y avait pas beaucoup de matériel auquel je pouvais me référer sur iOS, donc je l'ai implémenté moi-même, mais pour Android cet article a été très utile. Cependant, j'avais besoin de connaissances supplémentaires sur ce que je voulais mettre en œuvre cette fois, donc cet article l'inclut également.

Points de mise en œuvre

Initialisation en activité

Tout d'abord, l'activité principale. Ceci est ** juste une ligne, ajout de rubrique ** pour l'initialisation FCM. ** Si vous n'en avez même pas besoin, vous n'avez besoin de rien **, mais vous ne pourrez pas envoyer de notifications séparément entre ici et l'application de la version iOS.

MainActivity.java


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

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

        // other initializing...
    }

Obtenir un jeton d'appareil

Ensuite, récupérez le jeton d'appareil généré / mis à jour. Ceci est essentiel si vous voulez des notifications différentes pour différents périphériques [^ 1], mais l'heure exacte à laquelle vous pouvez le faire est FirebaseInstanceIdService # onTokenRefresh. Vous pouvez remplacer cela et envoyer le jeton d'appareil à votre propre serveur à ce moment-là.

Mais cette fois, j'ai juste besoin de pouvoir envoyer des notifications à tous les utilisateurs à la fois, et je ne sais pas si j'ai encore besoin du service FirebaseInstanceIdService. Si vous n'en avez pas besoin, ce sera ** encore une fois "vous n'avez rien à faire" **. Cependant, j'étais un peu mal à l'aise, alors j'ai décidé de ne laisser fonctionner que le service, donc je l'ai configuré pour démarrer la superclasse directement en tant que service sans créer de sous-classe. Est-ce correct?

AndroidManifest.xml


    <service android:name="com.google.firebase.iid.FirebaseInstanceIdService">
      <intent-filter>
        <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
      </intent-filter>
    </service>

Correspondance de réception au premier plan

Même dans FCM, article GCM que j'ai écrit dans mon travail précédent (chapitre de "balise qui peut automatiquement écraser et mettre à jour les notifications Android") -2.html) n'a pas modifié la spécification d'actualisation ** "Si vous recevez des notifications standard sur Android / uniquement en arrière-plan, vous n'avez pas besoin d'écrire de code" **. Mais cette fois, vous devez également gérer la notification au premier plan. Pour ce faire, vous pouvez remplacer FirebaseMessagingService # onMessageReceived et le faire. Parmi eux, vous pouvez générer le même contenu que la notification affichée en arrière-plan et l'enregistrer comme intention en attente.

Je veux dire, c'est l'article de @ kirimin C'est presque la même chose. C'est juste une tempête de pluie reconnaissante (mot mort). Cependant, seule la génération de l'intention lorsque la notification affichée par ce code est appuyée est légèrement modifiée. Fondamentalement, cette intention a glissé dans le processus, oh, cerveau COM (et pour une raison quelconque en tapant ceci) De plus, comme il n'est utilisé que dans la propre application, pas dans le cerveau pro-less où le doigt glisse et frappe sans problème), je me demande si la constante de chaîne de caractères doit être définie dans MainActivity et utilisée. C'est «MainActivity.ARG_FRAGMENT» et «MainActivity.PUSH_NOTIFICATION_ACTION».

FirMessagingService.java


public class FirMessagingService extends FirebaseMessagingService {
    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        Map<String, String> data = remoteMessage.getData();
        if (data == null) {
            return;
        }
        String fragment = data.get(MainActivity.ARG_FRAGMENT);
        if (fragment == null) {
            return;
        }

        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
        builder.setSmallIcon(R.drawable.notification);
        builder.setContentTitle(remoteMessage.getNotification().getTitle());
        builder.setContentText(remoteMessage.getNotification().getBody());
        builder.setDefaults(Notification.DEFAULT_SOUND
                | Notification.DEFAULT_VIBRATE
                | Notification.DEFAULT_LIGHTS);
        builder.setAutoCancel(true);

        //Créer une intention en attente
        Intent intent = new Intent(this, MainActivity.class);
        intent.setAction(MainActivity.PUSH_NOTIFICATION_ACTION);
        intent.putExtra(MainActivity.ARG_FRAGMENT, fragment);
        PendingIntent contentIntent = PendingIntent.getActivity(
                getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentIntent(contentIntent);

        //Affichage des notifications
        NotificationManagerCompat manager = NotificationManagerCompat.from(getApplicationContext());
        manager.notify(0, builder.build());
    }
}

Bien entendu, n'oubliez pas d'enregistrer cette sous-classe en tant que service.

AndroidManifest.xml


    <service android:name=".FirMessagingService">
      <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT"/>
      </intent-filter>
    </service>

Traitement lorsque la notification est activée

À ce propos, le règlement «Le mode de lancement est une tâche unique» fonctionne de manière extrêmement efficace, et il n'y a que deux modèles qui peuvent se produire lorsque vous appuyez sur une notification. (Notez que ce règlement n'a rien à voir avec FCM, c'est une méthode courante pour toujours démarrer une seule application sur Android, et c'est très courant [^ 2])

Activité Appelé lorsque vous appuyez sur
AppCompatActivityméthode de
Comment obtenir une intention
Au démarrage onNewIntent onNewIntentArguments de
Pas commencé onCreate getIntentMéthode

Et dans les deux cas, vous pouvez recevoir une intention contenant exactement les mêmes informations, vous pouvez donc la gérer avec une méthode commune.

MainActivity.java


    public static final String PUSH_NOTIFICATION_ACTION = "FCM";
    public static final String ARG_FRAGMENT = "fragment";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        FirebaseMessaging.getInstance().subscribeToTopic("TOPICNAME");
        int fragmentId = onIntent(getIntent());
        if (fragmentId == 0) {
            //Obtenez la valeur d'ID du fragment le plus récemment affiché
            fragmentId = PreferenceManager.getDefaultSharedPreferences(this).getInt(PreferenceKey.RECENT_FRAGMENT_POSITION, 0);
        }

        // other initializing

        changeFragment(fragmentId, true);
    }

    @Override
    public void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        changeFragment(onIntent(intent), false);
    }

    //Obtenez l'ID du fragment que vous souhaitez afficher à partir de l'intention de notification
    //Dans cette implémentation, pour les notifications{"fragment":"info"}Si le KV est inclus, j'essaye d'afficher l'écran d'information
    //Si vous utilisez la réflexion, vous n'avez pas besoin de branchement conditionnel,(^^ゞ
    private int onIntent(Intent intent) {
        if (intent != null && "info".equals(intent.getStringExtra(ARG_FRAGMENT))) {
            return R.id.info;
        } else {
            return 0;
        }
    }

    //Méthode unique pour basculer vers Fragment spécifié par fragmentId s'il existe
    //Si isFirst est true, affiche le Fragment par défaut si fragmentId est 0
    //Si faux, ne faites rien
    private void changeFragment(int fragmentId, boolean isFirst) {
        //Faisons de notre mieux
    }

Ensuite, définissez correctement le filtre d'intention afin que MainActivity puisse recevoir l'intention de la notification. La raison sera expliquée plus tard, mais le nom de l'action «" FCM "» doit être la même chaîne que ** «MainActivity.PUSH_NOTIFICATION_ACTION» **.

AndroidManifest.xml


    <activity android:label="@string/app_name" android:name=".MainActivity"
        android:taskAffinity="com.wsf_lp.oritsubushi.map" android:launchMode="singleTask">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      <intent-filter>
        <action android:name="FCM" />
        <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>
    </activity>

L'histoire la plus importante: comprendre la signification de divers littéraux de chaîne

En fait, si vous ne le faites pas correctement non seulement du côté de l'application mais aussi du côté de votre propre serveur, vous tomberez quelque part dans la série de mouvements «d'envoi et de réception de notifications et de changement d'affichage».

Donc, j'ai résumé la liste des littéraux de chaîne qui sont sortis jusqu'à présent dans la série de codes et leurs significations.

Chaîne Lieu d'apparence sens Le cas échéant
"FCM" Manifeste
Activité principale
Nom de l'action d'intention De la charge utile de notification FCMclick_actionLa valeur du
onMessageReceivedIntention créée avec
onNewIntentIntention transmise à
onCreateÀ l'intérieurgetIntentIntention obtenue en appelant
"fragment" FirebaseMessagingService
Activité principale
Nom de la clé de données utilisateur Nom de clé dans la charge de données FCM
onMessageReceivedNom de la clé dans les données distantes pouvant être obtenues avec
"info" FirebaseMessagingService
Activité principale
Valeur des données utilisateur De la charge utile des données FCM"fragment"La valeur du
onMessageReceivedで取得できるリモートデータ内La valeur du

C'est simple à résumer, mais "FCM payload" apparaît dans tous les éléments du tableau. En fait, il est très important de comprendre ** la charge utile sur votre propre serveur qui transmet les données à envoyer à FCM **. En d'autres termes, cela signifie également que ** lors de la liaison du fonctionnement de l'application avec des notifications push, l'omission de "saisir facilement le contenu de la notification depuis l'écran de notification de la console Firebase et cliquer (mot mort)" n'est pas autorisée **. (Bien sûr, compte tenu du problème d'icône décrit plus loin, la console actuelle est inutile depuis le début).

En ce qui concerne la charge utile FCM, Elle est répertoriée dans le document officiel, mais il ne suffit pas de simplement l'utiliser, [Tous les formats] Il semble indispensable de lire correctement (https://firebase.google.com/docs/cloud-messaging/http-server-ref).

Charge utile FCM

Dans cet esprit, voici les JSON que j'envoie de mon propre serveur au serveur FCM.

{
  "to": "Nom du sujet enregistré dans l'application",
  "notification": {
    "title": "Titre de la notification",
    "body": "Corps de notification",
    "click_action": "FCM",
    "icon": "notification"
  },
  "data": {
    "fragment": "info"
  }
}

La chose la plus importante dans FCM est la notification, la charge utile de notification. En plus de title et body, click_action est l'une des clés. Il s'agit exactement de la valeur de l'action contenue dans l'intention de notification push Android [^ 3]. Et avec la valeur de cette action, c'est le paramètre du filtre d'intention dans le manifeste qui empêche l'activité de l'application de recevoir des intentions supplémentaires. Par conséquent, * le click_action de la charge utile de notification envoyée au serveur FCM et le nom d'action du filtre d'intention défini dans le manifeste doivent correspondre *, et * l'intention générée par l'utilisateur d'afficher la notification même au premier plan. Les noms d'action doivent également être identiques *.

D'un autre côté, la "data", qui est facultative dans FCM, c'est-à-dire la charge utile de données, est un dictionnaire normal. Et c'est celui qui est passé tel quel au RemoteMessage qui est passé dans l'argument de FirebaseMessagingService # onMessageReceived, et il est également obtenu par l'argument de getIntent et ʻonNewIntent appelé à l'intérieur de ʻonCreate de l'activité. Il s'agit de la valeur définie comme le supplément de l'intention qui peut être effectuée. Donc * ces noms de clé doivent correspondre * et * la même valeur doit être copiée en tant qu'extra avec le même nom de clé dans l'intention qu'ils génèrent pour afficher la notification même au premier plan. *.

De plus, comme il ne sera pas internationalisé cette fois, c'est comme écrire du japonais tel qu'il est dans title et body, mais si l'internationalisation est nécessaire article GCM écrit dans mon travail précédent (: //www.mate-en.com/techblog/google-cloud-messaging-2.html) devrait être utile tel quel.

Icône de notification

Et encore une fois, mais pas directement lié au code. Sur Android, une icône peut être affichée dans l'affichage des notifications. Et dans les anciennes versions de GCM avant FCM, GCMListenerService # onMessageReceived est * toujours appelé * indépendamment du fait que l'application soit au premier plan ou en arrière-plan * (donc GCM ListerService doit être un service. Je ne l'ai pas fait), et j'ai toujours créé les notifications moi-même (juste dans le processus dans ʻonMessageReceived au premier plan dans le FCM actuel). Bien sûr, j'étais libre d'y définir l'icône de notification. Cependant, le nouveau GCM après cela, qui est presque le FCM actuel, a introduit la spécification que * ʻonMessageReceived n'est pas appelé en arrière-plan * ". Et par conséquent, vous ne pourrez pas définir l'icône en arrière-plan. Pour éviter cela, le nom de clé «icône» a été ajouté à la charge utile de notification de GCM, et il est transféré au FCM actuel.

{
  "notification": {
    "icon": "notification"
  }
}

Et c'est aussi une métamorphose, mais supposons que vous définissiez la valeur notification sur ʻiconcomme ci-dessus. Cette valeur de «notification» a été mappée sur «R.drawable.notification» du côté de l'application. Bien sûr, ce symbole est une valeur d'identification, et si l'icône est un fichier image, en particulier un fichier appeléres / drawable / notification.png (bien sûr, la substance a été divisée par résolution / version SDK / local, etc. (Distribué en dossiers) est utilisé comme icône. Le fait est que ** le nom de fichier (à l'exclusion de l'extension) de l'image PNG pour l'icône sera la valeur de la clé ʻicon de la charge utile de notification **. En d'autres termes, pour afficher l'icône de notification, vous pouvez généralement créer (beaucoup!) D'images d'icônes, les placer de manière appropriée sous res / drawable / et transmettre le nom de fichier au serveur FCM. Bien sûr, la même icône doit être placée dans ʻonMessageReceived` pour afficher la notification même au premier plan.

Ce sentiment que "la chaîne de caractères du nom de fichier devient un nom de symbole dans le code Java et il devient une valeur entière lorsqu'elle est référencée, mais lors de l'appel depuis le serveur, spécifiez-le avec une chaîne de caractères", il fait le tour et revient au sentiment d'origine .. Comme prévu, il s'agit d'Android (poitrine brûlée) [^ 4].

Conclusion

Remarque

[^ 1]: s'il s'agit d'une application SNS ou de chat, le contenu à notifier dépend de l'utilisateur. Par conséquent, les jetons de périphérique de l'utilisateur et du terminal (bien sûr, le même utilisateur peut utiliser plusieurs périphériques, donc RDB le gère dans la table n: m) sont liés du côté du serveur pour notifier un utilisateur spécifique. Le processus d'envoi uniquement à tous les appareils de cet utilisateur est requis. Et cette notification de l'appareil de cet utilisateur particulier ne nécessite qu'un jeton d'appareil, et Android peut changer ce jeton d'appareil, donc chaque fois qu'il change, il informe son propre serveur. Doit être. [^ 2]: Ah, à l'ère Win32, cela a été fait avec un Mutex nommé ... La première version de Windows Eclipse a juste trop cliqué sur l'icône et a démarré plusieurs fois, donc pour éviter de casser l'espace de travail, un lanceur dédié Je l'ai écrit avec CreateMutex ... (À cause de vieux yeux, de yeux lointains) [^ 3]: Au fait, click_action, qui est un bouton qui apparaît lorsque vous faites glisser une notification sur iOS (si l'application le prend en charge) = est lié à une action personnalisée. C'est une clé héritée de GCM, mais il est étonnant qu'elle soit compatible avec les deux OS! C'est un éloge. [^ 4]: Bien sûr, la conversion de symboles en valeurs entières et la construction auraient été nécessaires sur les anciens Androïdes, qui avaient des ressources incroyablement pauvres. Et même à l'ère actuelle où ce n'est pas le cas, il y a un avantage que l'achèvement du code de l'EDI fonctionne facilement (même dans l'ancien temps, il a été abusé comme un mauvais savoir-faire de VC ++ en fourrant tout dans une classe mystérieuse). Non, mais je pense aussi qu'une partie importante du caractère déraisonnable de l'API Win32 est qu'elle a été trop traînée par Win16 pour rendre le code des contrôles OLE que la société a fait en interne pour VB compatible (ou plutôt mal). La racine peut être VB plus ancienne que Win16 ??), Android est également entraîné par diverses choses comme la limitation de la méthode 64k (du coup, il est très difficile de faire Scala) ... Est-ce quelque chose qui a une histoire? ..

Recommended Posts

J'ai essayé d'implémenter la notification push Firebase en Java
J'ai essayé d'implémenter la méthode de division mutuelle d'Eugrid en Java
J'ai essayé d'implémenter des relations polymorphes à Nogizaka.
J'ai essayé de sortir quatre-vingt-dix-neuf en Java
J'ai essayé de créer une compétence Alexa avec Java
J'ai essayé la métaprogrammation avec Java
J'ai essayé d'implémenter TCP / IP + BIO avec JAVA
# 2 [Note] J'ai essayé de calculer quatre-vingt-dix-neuf avec Java.
J'ai essayé de créer une compétence Clova en Java
J'ai essayé de créer une fonction de connexion avec Java
J'ai essayé d'implémenter Sterling Sort avec Java Collector
[Java] J'ai essayé de mettre en œuvre la recherche de produits de l'API Yahoo
~ J'ai essayé d'apprendre la programmation fonctionnelle avec Java maintenant ~
J'ai essayé de découvrir ce qui avait changé dans Java 9
J'ai essayé d'interagir avec Java
J'ai essayé d'utiliser JWT en Java
J'ai essayé de résumer l'apprentissage Java (1)
Essayez d'implémenter Yuma en Java
J'ai essayé de résumer Java 8 maintenant
J'ai essayé de convertir une chaîne de caractères en un type LocalDate en Java
J'ai essayé d'utiliser Dapr en Java pour faciliter le développement de microservices
J'ai essayé d'implémenter une application web pleine de bugs avec Kotlin
J'ai créé un client RESAS-API en Java
J'ai essayé d'utiliser l'API Elasticsearch en Java
Comment implémenter le calcul de la date en Java
Comment implémenter le filtre de Kalman par Java
J'ai essayé d'implémenter le traitement Ajax de la fonction similaire dans Rails
Essayez d'implémenter l'ajout n-aire en Java
J'ai essayé de résumer les expressions Java lambda
J'ai essayé le nouveau yuan à Java
J'ai essayé de configurer les débutants Java pour qu'ils utilisent des touches de raccourci dans eclipse
J'ai essayé d'implémenter le modèle Iterator
Comment appliquer les conventions de codage en Java
J'ai essayé de créer une application de conversation en Java à l'aide de l'IA «A3RT»
J'ai essayé de faire une authentification de base avec Java
Je souhaite envoyer un e-mail en Java.
J'ai essayé d'organiser la session en Rails
java j'ai essayé de casser un simple bloc
Envoyer des notifications push à l'aide de Notification Hubs en Java
Je voulais que (a == 1 && a == 2 && a == 3) vrai en Java
rsync4j --Je veux toucher rsync en Java.
J'ai essayé de développer une application en 2 langues
J'ai essayé d'implémenter un serveur en utilisant Netty
J'ai essayé de casser le bloc avec java (1)
Essayé l'API Toot et Streaming de Mastodon en Java
J'ai essayé d'implémenter le téléchargement de fichiers avec Spring MVC
Je veux faire quelque chose comme "cls" en Java
[Java 11] J'ai essayé d'exécuter Java sans compiler avec javac
[Java] J'ai essayé de résoudre le problème de rang B de Paiza
J'ai essayé de faire fonctionner SQS en utilisant AWS Java SDK
Je veux aussi utiliser ES2015 avec Java! → (´ ・ ω ・ `)
J'ai essayé d'utiliser l'instruction Extended for en Java
Résumé de la mise en œuvre des arguments par défaut en Java
J'ai essayé de passer Java Silver en 2 semaines sans connaître Java