127 votes

Firebase FCM force onTokenRefresh() à être appelé

Je suis en train de migrer mon application de GCM à FCM.

Lorsqu'un nouvel utilisateur installe mon application, l'application onTokenRefresh() est automatiquement appelé. Le problème est que l'utilisateur n'est pas encore connecté (pas d'ID utilisateur).

Comment puis-je déclencher le onTokenRefresh() après que l'utilisateur se soit connecté ?

2 votes

Une question très similaire a déjà été posée dans le lien suivant. Vérifiez si la réponse vous est utile : stackoverflow.com/questions/37517254/

186voto

Prince Points 1664

El onTokenRefresh() va être appelée à chaque fois qu'un nouveau jeton est généré. Lors de l'installation de l'application, il sera généré immédiatement (comme vous l'avez constaté). Elle sera également appelée lorsque le jeton aura changé.

Selon le FirebaseCloudMessaging guide :

Vous pouvez cibler les notifications sur un appareil unique et spécifique. Au premier démarrage initial de votre application, le SDK FCM génère un jeton d'enregistrement pour l'instance de l'application cliente.

Screenshot

Lien vers la source : https://firebase.google.com/docs/notifications/Android/console-device#access_the_registration_token

Cela signifie que l'enregistrement du jeton se fait par application. Il semble que vous souhaitiez utiliser le jeton après que l'utilisateur se soit connecté. Ce que je vous suggère, c'est d'enregistrer le jeton dans le fichier onTokenRefresh() au stockage interne ou aux préférences partagées. Ensuite, récupérez le jeton du stockage après qu'un utilisateur s'est connecté et enregistrez le jeton avec votre serveur si nécessaire.

Si vous souhaitez forcer manuellement le onTokenRefresh() vous pouvez créer un IntentService et supprimer l'instance de jeton. Ensuite, lorsque vous appelez getToken, l'objet onTokenRefresh() sera à nouveau appelée.

Exemple de code :

public class DeleteTokenService extends IntentService
{
    public static final String TAG = DeleteTokenService.class.getSimpleName();

    public DeleteTokenService()
    {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(Intent intent)
    {
        try
        {
            // Check for current token
            String originalToken = getTokenFromPrefs();
            Log.d(TAG, "Token before deletion: " + originalToken);

            // Resets Instance ID and revokes all tokens.
            FirebaseInstanceId.getInstance().deleteInstanceId();

            // Clear current saved token
            saveTokenToPrefs("");

            // Check for success of empty token
            String tokenCheck = getTokenFromPrefs();
            Log.d(TAG, "Token deleted. Proof: " + tokenCheck);

            // Now manually call onTokenRefresh()
            Log.d(TAG, "Getting new token");
            FirebaseInstanceId.getInstance().getToken();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    private void saveTokenToPrefs(String _token)
    {
        // Access Shared Preferences
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        SharedPreferences.Editor editor = preferences.edit();

        // Save to SharedPreferences
        editor.putString("registration_id", _token);
        editor.apply();
    }

    private String getTokenFromPrefs()
    {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        return preferences.getString("registration_id", null);
    }
}

EDITAR

FirebaseInstanceIdService

public class FirebaseInstanceIdService extends Service

Cette classe est dépréciée. En faveur de la substitution de onNewToken dans FirebaseMessagingService. Une fois que cela a été implémenté, ce service peut être supprimé en toute sécurité.

onTokenRefresh() est déprécié . Utilisez onNewToken() en MyFirebaseMessagingService

public class MyFirebaseMessagingService extends FirebaseMessagingService {

@Override
public void onNewToken(String s) {
    super.onNewToken(s);
    Log.e("NEW_TOKEN",s);
    }

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    super.onMessageReceived(remoteMessage);
    }
}

0 votes

Oui, c'est ce que j'ai fait hier. J'ai juste enregistré le jeton et l'ai envoyé avec l'enregistrement.

0 votes

Réponse acceptée. Même si je ne voulais pas de "solutions de rechange", je suppose que Firebase ne donne pas la possibilité de faire autrement. Merci !

34 votes

Même si onTokenRefresh() est appelé avant que l'utilisateur ne se connecte, au lieu de faire le travail de stockage local, lorsque l'utilisateur se connecte, vous pouvez récupérer le jeton en utilisant FirebaseInstanceId.getInstance().getToken() et l'envoyer au serveur pour l'enregistrement. (à moins que vous ne vouliez le stocker localement pour supprimer un ancien jeton de votre serveur).

18voto

pRaNaY Points 2342

Essayez de mettre en œuvre FirebaseInstanceIdService pour obtenir un jeton de rafraîchissement.

Accéder au jeton d'enregistrement :

Vous pouvez accéder à la valeur du jeton en étendant l'application FirebaseInstanceIdService . Assurez-vous que vous avez ajouté le service à votre manifeste puis appeler getToken dans le cadre de onTokenRefresh et enregistrez la valeur comme indiqué :

     @Override
public void onTokenRefresh() {
    // Get updated InstanceID token.
    String refreshedToken = FirebaseInstanceId.getInstance().getToken();
    Log.d(TAG, "Refreshed token: " + refreshedToken);

    // TODO: Implement this method to send any registration to your app's servers.
    sendRegistrationToServer(refreshedToken);
}

Code complet :

   import android.util.Log;

import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;

public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {

    private static final String TAG = "MyFirebaseIIDService";

    /**
     * Called if InstanceID token is updated. This may occur if the security of
     * the previous token had been compromised. Note that this is called when the InstanceID token
     * is initially generated so this is where you would retrieve the token.
     */
    // [START refresh_token]
    @Override
    public void onTokenRefresh() {
        // Get updated InstanceID token.
        String refreshedToken = FirebaseInstanceId.getInstance().getToken();
        Log.d(TAG, "Refreshed token: " + refreshedToken);

        // TODO: Implement this method to send any registration to your app's servers.
        sendRegistrationToServer(refreshedToken);
    }
    // [END refresh_token]

    /**
     * Persist token to third-party servers.
     *
     * Modify this method to associate the user's FCM InstanceID token with any server-side account
     * maintained by your application.
     *
     * @param token The new token.
     */
    private void sendRegistrationToServer(String token) {
        // Add custom implementation, as needed.
    }
}

Voir ma réponse aquí .

EDITS :

Vous ne devriez pas commencer un FirebaseInstanceIdService vous-même.

Il se déclenchera lorsque le système déterminera que les jetons doivent être rafraîchis. L'application doit appeler getToken() et envoyer les jetons à tous les serveurs d'application.

Cette fonction ne sera pas appelée très fréquemment, mais elle est nécessaire pour la rotation des clés et pour gérer les changements d'ID d'instance dus à.. :

  • L'application supprime l'ID de l'instance
  • L'application est restaurée sur un nouvel appareil Utilisateur
  • désinstaller/réinstaller l'application
  • L'utilisateur efface les données de l'application

Le système limitera l'événement de rafraîchissement sur tous les appareils pour éviter de surcharger les serveurs d'application avec des mises à jour de jetons.

Essayez la méthode ci-dessous :

vous appelleriez FirebaseInstanceID.getToken() n'importe où en dehors de votre principal (qu'il s'agisse d'un service, d'une tâche AsyncTask, etc. et l'envoyer à votre serveur. Ensuite, chaque fois que onTokenRefresh() est appelé, vous appelleriez FirebaseInstanceID.getToken() obtenir un nouveau jeton et l'envoyer au serveur (en incluant probablement l'ancien jeton pour que le serveur puisse le supprimer et le remplacer par le nouveau). pour que votre serveur puisse le supprimer et le remplacer par le nouveau jeton).

2 votes

J'ai implémenté FirebaseInstanceIdService, le problème est que onTokenRefresh() est appelé presque immédiatement après que l'utilisateur ait installé l'application. J'ai besoin qu'il soit appelé après avoir effectué un Login/Signup.

1 votes

Ainsi, la suppression du FirebaseInstanceId rafraîchira le jeton, merci !

0 votes

Après le passage de GCM à FCM, FirebaseInstanceId.getInstance().getToken() ; retourne toujours null. Une solution ?

3voto

Manav Patadia Points 186

Les gars, la solution est très simple

https://developers.google.com/instance-id/guides/Android-implementation#generate_a_token

Remarque : si votre application a utilisé des jetons qui ont été supprimés par deleteInstanceID, votre application devra générer des jetons de remplacement.

Au lieu de supprimer l'Id de l'instance, supprimez seulement le jeton :

String authorizedEntity = PROJECT_ID;
String scope = "GCM";
InstanceID.getInstance(context).deleteToken(authorizedEntity,scope);

2 votes

Cela n'a pas fonctionné pour moi. Après avoir appelé deleteToken(), getToken() retourne le même jeton qu'avant et onTokenRefresh n'a pas été appelé.

2voto

Je maintiens un drapeau dans les préférences partagées qui indique si le jeton gcm est envoyé au serveur ou non. Dans l'écran Splash, j'appelle à chaque fois une méthode sendDevicetokenToServer. Cette méthode vérifie si l'identifiant de l'utilisateur n'est pas vide et si le statut d'envoi de gcm est atteint, puis envoie le jeton au serveur.

public static void  sendRegistrationToServer(final Context context) {

if(Common.getBooleanPerf(context,Constants.isTokenSentToServer,false) ||
        Common.getStringPref(context,Constants.userId,"").isEmpty()){

    return;
}

String token =  FirebaseInstanceId.getInstance().getToken();
String userId = Common.getUserId(context);
if(!userId.isEmpty()) {
    HashMap<String, Object> reqJson = new HashMap<>();
    reqJson.put("deviceToken", token);
    ApiInterface apiService =
            ApiClient.getClient().create(ApiInterface.class);

    Call<JsonElement> call = apiService.updateDeviceToken(reqJson,Common.getUserId(context),Common.getAccessToken(context));
    call.enqueue(new Callback<JsonElement>() {
        @Override
        public void onResponse(Call<JsonElement> call, Response<JsonElement> serverResponse) {

            try {
                JsonElement jsonElement = serverResponse.body();
                JSONObject response = new JSONObject(jsonElement.toString());
                if(context == null ){
                    return;
                }
                if(response.getString(Constants.statusCode).equalsIgnoreCase(Constants.responseStatusSuccess)) {

                    Common.saveBooleanPref(context,Constants.isTokenSentToServer,true);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(Call<JsonElement> call, Throwable throwable) {

            Log.d("", "RetroFit2.0 :getAppVersion: " + "eroorrrrrrrrrrrr");
            Log.e("eroooooooorr", throwable.toString());
        }
    });

}

}

Dans la classe MyFirebaseInstanceIDService

    @Override
public void onTokenRefresh() {
    // Get updated InstanceID token.
    String refreshedToken = FirebaseInstanceId.getInstance().getToken();
    Log.d(TAG, "Refreshed token: " + refreshedToken);

    // If you want to send messages to this application instance or
    // manage this apps subscriptions on the server side, send the
    // Instance ID token to your app server.
    Common.saveBooleanPref(this,Constants.isTokenSentToServer,false);
    Common.sendRegistrationToServer(this);
    FirebaseMessaging.getInstance().subscribeToTopic("bloodRequest");
}

0voto

Varun Garg Points 1103

Cette réponse ne détruit pas l'identifiant de l'instance, elle est capable de récupérer l'identifiant actuel. Elle stocke également le nouveau dans les préférences partagées.

Strings.xml

<string name="pref_firebase_instance_id_key">pref_firebase_instance_id</string>
<string name="pref_firebase_instance_id_default_key">default</string>

Utility.java (toute classe où vous voulez définir/obtenir des préférences)

public static void setFirebaseInstanceId(Context context, String InstanceId) {
    SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
    SharedPreferences.Editor editor;
    editor = sharedPreferences.edit();
    editor.putString(context.getString(R.string.pref_firebase_instance_id_key),InstanceId);
    editor.apply();
}

public static String getFirebaseInstanceId(Context context) {
    SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
    String key = context.getString(R.string.pref_firebase_instance_id_key);
    String default_value = context.getString(R.string.pref_firebase_instance_id_default_key);
    return sharedPreferences.getString(key, default_value);
}

MyFirebaseInstanceIdService.java (étend FirebaseInstanceIdService)

@Override
public void onCreate()
{
    String CurrentToken = FirebaseInstanceId.getInstance().getToken();

    //Log.d(this.getClass().getSimpleName(),"Inside Instance on onCreate");
    String savedToken = Utility.getFirebaseInstanceId(getApplicationContext());
    String defaultToken = getApplication().getString(R.string.pref_firebase_instance_id_default_key);

    if(CurrentToken != null && !savedToken.equalsIgnoreCase(defaultToken))
    //currentToken is null when app is first installed and token is not available
    //also skip if token is already saved in preferences...
    {
        Utility.setFirebaseInstanceId(getApplicationContext(),CurrentToken);
    }
    super.onCreate();
}

@Override
public void onTokenRefresh() {
     .... prev code
      Utility.setFirebaseInstanceId(getApplicationContext(),refreshedToken);
     ....

}

Android 2.0 et plus onCreate de service n'est pas invoqué lorsqu'il est lancé automatiquement ( source ). Au lieu de cela, onStartCommand est surchargée et utilisée. Mais dans le FirebaseInstanceIdService actuel, il est déclaré comme final et ne peut pas être surchargé. Cependant, lorsque nous démarrons le service en utilisant startService(), si le service est déjà en cours d'exécution, sa fonction l'instance originale est utilisée (ce qui est bien). Notre onCreate() (défini plus haut) a également été invoqué !

Utilisez-le au début de MainActivity ou à n'importe quel moment où vous pensez avoir besoin de l'identifiant d'instance.

MyFirebaseInstanceIdService myFirebaseInstanceIdService = new MyFirebaseInstanceIdService();
Intent intent= new Intent(getApplicationContext(),myFirebaseInstanceIdService.getClass());
//Log.d(this.getClass().getSimpleName(),"Starting MyFirebaseInstanceIdService");
startService(intent); //invoke onCreate

Et enfin,

Utility.getFirebaseInstanceId(getApplicationContext())

Note Vous pouvez encore améliorer ce système en essayant de déplacer le code startservice() vers la méthode getFirebaseInstanceId.

0 votes

Si vous réinitialisez l'application /run la première fois, il faut un certain temps pour qu'elle rafraîchisse le jeton. Vous obtiendrez donc la chaîne "default" pendant une minute ou deux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X