web-dev-qa-db-fra.com

Firebase Cloud Messaging - Gestion de la déconnexion

Comment gérer la situation lorsque l'utilisateur se déconnecte de mon application et que je ne souhaite plus qu'il reçoive de notifications sur le périphérique.

J'ai essayé

FirebaseInstanceId.getInstance().deleteToken(FirebaseInstanceId.getInstance().getId(), FirebaseMessaging.INSTANCE_ID_SCOPE)

Mais je reçois toujours les notifications sur le registration_id De mon appareil.

Je me suis également assuré que c'était le jeton que je devais supprimer:

FirebaseInstanceId.getInstance().getToken(FirebaseInstanceId.getInstance().getId(), FirebaseMessaging.INSTANCE_ID_SCOPE)

ou simplement FirebaseInstanceId.getInstance().getToken()).

J'ai aussi essayé FirebaseInstanceId.getInstance().deleteInstanceId(), mais la prochaine fois que j'appelle FirebaseInstanceId.getInstance.getToken, Je reçois null (cela fonctionne à la deuxième tentative).

Je suppose que, après deleteInstanceId, je pourrais immédiatement appeler à nouveau getToken(), mais cela ressemble à un hack. Et aussi il y a cette réponse qui indique que cela ne devrait pas être fait, mais propose de supprimer le jeton qui apparemment ne fonctionne pas.

Alors, quelle est la bonne méthode pour gérer cela?

56
Michał K

D'accord. J'ai donc réussi à faire des tests et j'ai conclu ce qui suit:

  1. deleteToken() est la contrepartie de getToken(String, String), mais pas pour getToken().

Cela ne fonctionne que si l'identifiant de l'expéditeur que vous transmettez est un identifiant d'expéditeur différent (et non le même identifiant que vous pouvez voir dans votre google-services.json). Par exemple, si vous souhaitez autoriser un serveur différent à envoyer vers votre application, vous appelez getToken("THEIR_SENDER_ID", "FCM") pour leur donner autorisation à envoyer vers votre application. Cela renverra un jeton d'enregistrement différent qui correspond uniquement à cet expéditeur spécifique.

À l'avenir, si vous choisissez de supprimer leur autorisation pour l'envoyer à votre application, vous devrez alors utiliser deleteToken("THEIR_SENDER_ID", "FCM"). Cela invalidera le jeton correspondant et, lorsque l'expéditeur tentera d'envoyer un message, conformément au comportement souhaité, il recevra une erreur NotRegistered.

  1. Afin de supprimer le jeton de votre propre expéditeur, le traitement correct consiste à utiliser deleteInstanceId().

Mention spéciale à ce sujet réponse de @Prince , en particulier l’exemple de code qui m’a aidé à cet égard.

Comme @ MichałK le fait déjà dans son message, après avoir appelé la deleteInstanceId(), getToken() devrait être appelée afin d'envoyer une demande pour un nouveau jeton. Cependant, vous n'êtes pas obligé de l'appeler une deuxième fois. Tant que onTokenRefresh() onNewToken() est implémenté, il devrait se déclencher automatiquement en vous fournissant le nouveau jeton.

Pour résumer, deleteInstanceId()> getToken()> check onTokenRefresh() onNewToken().

Remarque : L'appel de deleteInstanceId() ne supprimera pas uniquement le jeton de votre propre application. Il supprimera tous les abonnements à des sujets et tous les autres jetons associés à l'instance d'application.


Etes-vous certain d'appeler correctement deleteToken()? La valeur pour le public doit être (voir aussi dans ma réponse que vous avez liée) est "définie sur l'ID de l'expéditeur du serveur d'applications". Vous transmettez la valeur getId() qui n'est pas identique à l'ID de l'expéditeur (elle contient la valeur de l'ID d'instance d'application). En outre, comment envoyez-vous le message (serveur d'applications ou console de notifications)?

getToken() et getToken(String, String) renvoie des jetons différents. Voir ma réponse ici .

J'ai aussi essayé FirebaseInstanceId.getInstance().deleteInstanceId(), mais la prochaine fois que j'appellerai FirebaseInstanceId.getInstance.getToken, Je reçois la valeur null (cela fonctionne au deuxième essai).

C'est probablement parce que la première fois que vous appelez la getToken(), elle est toujours générée. C'est juste le comportement prévu.

Je suppose qu'après deleteInstanceId je pourrais immédiatement appeler getToken() à nouveau, mais cela ressemble à un hack.

Pas vraiment. C'est ainsi que vous obtiendrez le nouveau jeton généré (à condition qu'il soit déjà généré). Donc je pense que ça va.

42
AL.

Je travaillais sur le même problème, quand j'avais fait mon logout() à partir de mon application. Mais le problème était qu'après la déconnexion, je recevais toujours les notifications Push de Firebase. J'ai essayé de supprimer le jeton Firebase. Mais après avoir supprimé le jeton dans ma méthode logout(), c'est null lorsque je l'interroge dans ma méthode login(). Après 2 jours de travail, j'ai finalement eu une solution.

  1. Dans votre méthode logout(), supprimez le jeton Firebase en arrière-plan, car vous ne pouvez pas supprimer le jeton Firebase du thread principal.

    new AsyncTask<Void,Void,Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            try
            {
                FirebaseInstanceId.getInstance().deleteInstanceId();
            } catch (IOException e)
            {
                e.printStackTrace();
            }
            return null;
        }
    
        @Override
        protected void onPostExecute(Void result) {
            // Call your Activity where you want to land after log out
        }
    }.execute();
    
  2. Dans votre méthode login(), générez à nouveau le jeton Firebase.

    new AsyncTask<Void,Void,Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            String token = FirebaseInstanceId.getInstance().getToken();
            // Used to get firebase token until its null so it will save you from null pointer exeption
            while(token == null) {
                token = FirebaseInstanceId.getInstance().getToken();
            }
            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
        }
    }.execute();
    
18
Sunil

J'ai fait une brève recherche sur la solution la plus élégante pour récupérer le contrôle total (abonnement et désabonnement à FCM) comme avant. Activer et désactiver le FCM après la connexion ou la déconnexion de l'utilisateur.

Étape 1. - Empêcher l'initialisation automatique

Firebase gère maintenant le InstanceID et tout ce qui nécessite de générer un jeton d’enregistrement. Avant tout, vous devez empêcher initialisation automatique. En fonction de la documentation d'installation officielle , vous devez ajouter ces valeurs méta-données à votre AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<application>

  <!-- FCM: Disable auto-init -->
  <meta-data Android:name="firebase_messaging_auto_init_enabled"
             Android:value="false" />
  <meta-data Android:name="firebase_analytics_collection_enabled"
             Android:value="false" />

  <!-- FCM: Receive token and messages -->
  <service Android:name=".FCMService">
    <intent-filter>
        <action Android:name="com.google.firebase.MESSAGING_EVENT"/>
    </intent-filter>
  </service>

</application>

Vous avez maintenant désactivé le processus de demande automatique de jeton. En même temps, vous avez la possibilité de l'activer à nouveau par code à l'exécution.

Étape 2. - Implémentez enableFCM() et disableFCM() fonctions

Si vous réactivez l'initialisation automatique, vous recevez immédiatement un nouveau jeton. Il s'agit donc d'un moyen idéal pour implémenter la méthode enableFCM(). Toutes les informations d'abonnement attribuées à InstanceID. Par conséquent, lorsque vous le supprimez, initiez-vous à désabonner tous les sujets. De cette façon, vous pouvez implémenter la méthode disableFCM(), désactivez simplement auto-init avant de le supprimer.

public class FCMHandler {

    public void enableFCM(){
        // Enable FCM via enable Auto-init service which generate new token and receive in FCMService
        FirebaseMessaging.getInstance().setAutoInitEnabled(true);
    }

    public void disableFCM(){
        // Disable auto init
        FirebaseMessaging.getInstance().setAutoInitEnabled(false);
        new Thread(() -> {
            try {
                // Remove InstanceID initiate to unsubscribe all topic
                // TODO: May be a better way to use FirebaseMessaging.getInstance().unsubscribeFromTopic()
                FirebaseInstanceId.getInstance().deleteInstanceId();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }

}

Étape 3. - FCMService implementation - jeton et réception de message

Dans la dernière étape, vous devez recevoir le nouveau jeton et l’envoyer directement à votre serveur. D'autre part, vous recevrez votre message de données et faites-le comme vous voulez.

public class FCMService extends FirebaseMessagingService {

    @Override
    public void onNewToken(String token) {
        super.onNewToken(token);
        // TODO: send your new token to the server
    }

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);
        String from = remoteMessage.getFrom();
        Map data = remoteMessage.getData();
        if (data != null) {
            // TODO: handle your message and data
            sendMessageNotification(message, messageId);
        }
    }

    private void sendMessageNotification(String msg, long messageId) {
        // TODO: show notification using NotificationCompat
    }
}

Je pense que cette solution est claire, simple et transparente. J'ai testé dans un environnement de production et ça marche. J'espère que c'était utile.

17

Les développeurs ne doivent jamais désinscrire l'application client en tant que mécanisme de déconnexion ou de commutation entre utilisateurs, pour les raisons suivantes:

  • Un jeton d'enregistrement n'est pas associé à un utilisateur connecté particulier. Si l'application cliente se désenregistre puis se réenregistre, elle peut recevoir le même jeton d'enregistrement ou un autre jeton d'enregistrement.
  • La désinscription et le réenregistrement peuvent prendre chacun cinq minutes pour se propager. Pendant ce temps, des messages peuvent être rejetés en raison de l'état non enregistré, et des messages peuvent être envoyés au mauvais utilisateur. Pour vous assurer que les messages vont à l'utilisateur prévu:

  • Le serveur d'applications peut gérer un mappage entre l'utilisateur actuel et le jeton d'enregistrement.

  • L'application client peut ensuite vérifier que les messages reçus correspondent à l'utilisateur connecté.

il s'agissait d'une documentation google obsolète: https://developers.google.com/cloud-messaging/registration#unregistration-and-unsubscription

mais il y a des raisons de croire que cela est encore disponible

vérifier ce code firebase https://github.com/firebase/functions-samples/blob/master/fcm-notifications/functions/index.js

et celui-ci https://github.com/firebase/friendlychat-web/blob/master/cloud-functions/public/scripts/main.js

5
Dan Alboteanu

Étant donné que getToken() est obsolète, utilisez getInstanceId() pour régénérer un nouveau jeton à la place. Cela a le même effet.

public static void resetInstanceId() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                FirebaseInstanceId.getInstance().deleteInstanceId();
                FirebaseInstanceId.getInstance().getInstanceId();   
                Helper.log(TAG, "InstanceId removed and regenerated.");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
}
4
Reedy Creeker

Je sais que je suis en retard pour la fête. deleteInstanceId() devrait être appelé depuis le thread d'arrière-plan puisqu'il s'agit d'un appel bloquant. Il suffit de vérifier la méthode deleteInstanceId() dans FirebaseInstanceId () classe.

@WorkerThread
public void deleteInstanceId() throws IOException {
    if (Looper.getMainLooper() == Looper.myLooper()) {
        throw new IOException("MAIN_THREAD");
    } else {
        String var1 = zzh();
        this.zza(this.zzal.deleteInstanceId(var1));
        this.zzl();
    }
}  

Vous pouvez démarrer un service IntentService pour supprimer l'ID d'instance et les données qui lui sont associées.

2
Sarweshkumar C R