Firebase FCM force onTokenRefresh () à être appelé


111

Je migre mon application de GCM vers FCM.

Lorsqu'un nouvel utilisateur installe mon application, le 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 la connexion de l'utilisateur?


1
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/…
Diego Giorgini

Réponses:


172

La onTokenRefresh()méthode sera appelée chaque fois qu'un nouveau jeton est généré. Lors de l'installation de l'application, elle sera générée immédiatement (comme vous l'avez constaté). Il sera également appelé lorsque le jeton aura changé.

D'après le FirebaseCloudMessagingguide:

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

Capture d'écran

Lien 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 la connexion d'un utilisateur. Ce que je suggérerais, c'est que vous enregistriez le jeton dans la onTokenRefresh()méthode dans le stockage interne ou dans les préférences partagées. Ensuite, récupérez le jeton du stockage une fois qu'un utilisateur s'est connecté et enregistrez le jeton auprès de 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, la onTokenRefresh()méthode 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);
    }
}

ÉDITER

FirebaseInstanceIdService

classe publique FirebaseInstanceIdService étend le service

Cette classe est obsolète. En faveur de la substitution de onNewToken dans FirebaseMessagingService. Une fois que cela a été mis en œuvre, ce service peut être supprimé en toute sécurité.

onTokenRefresh () est obsolète . Utiliser onNewToken()dansMyFirebaseMessagingService

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);
    }
} 

27
Même si onTokenRefresh () est appelé avant que l'utilisateur ne soit connecté, au lieu d'effectuer 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. (sauf si vous souhaitez le stocker localement pour supprimer un ancien jeton de votre serveur)
geekoraul

10
FireBase est intelligent et il appellera la méthode onTokenRefresh (), uniquement s'il n'y a pas de jeton (il est supprimé ou il est appelé pour la première fois) ou quelque chose d'autre se produit et le jeton a été changé. Si quelqu'un veut appeler onTokenRefresh peut supprimer le jeton, puis appeler FirebaseInstanceId.getInstance (). GetToken (). L'opération FirebaseInstanceId.getInstance (). DeleteInstanceId () doit être dans AsyncTask ou nouveau Thread, elle ne peut pas être sur MainThread !!!
Stoycho Andreev

3
Pourquoi ne pas simplement appeler FirebaseInstanceId.getToken?
esong

1
Eh bien, a parfaitement fonctionné lors de l'appel dans IntentService, et pas besoin de sauvegarder le jeton dans les préférences, je suppose. Étant donné que la valeur ne change pas tant que FirebaseInstanceId.getInstance (). DeleteInstanceId (); est appelé. Kinda a sauvé ma journée. :)
Detoxic-Soul

5
Pourquoi enregistrer le jeton dans les préférences partagées - si vous pouvez appeler FirebaseInstanceId.getInstance (). GetToken () à tout moment pour obtenir sa valeur?
Alexander Farber

18

Essayez de mettre en œuvre FirebaseInstanceIdServicepour obtenir un jeton d'actualisation.

Accédez au jeton d'inscription:

Vous pouvez accéder à la valeur du jeton en étendant FirebaseInstanceIdService . Assurez-vous que vous avez ajouté le service à votre manifeste , puis appelez getTokendans le contexte de onTokenRefreshet 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 ici .

MODIFIER:

Vous ne devriez pas démarrer vous-même un FirebaseInstanceIdService .

Il sera appelé lorsque le système déterminera que les jetons doivent être actualisés. L'application doit appeler getToken () et envoyer les jetons à tous les serveurs d'applications.

Cela ne sera pas appelé très fréquemment, il est nécessaire pour la rotation des clés et pour gérer les changements d'ID d'instance en raison de:

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

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

Essayez ci-dessous :

vous appelleriez FirebaseInstanceID.getToken () n'importe où hors de votre thread principal (qu'il s'agisse d'un service, AsyncTask, etc.), stockez le jeton retourné localement et envoyez-le à votre serveur. Ensuite, à chaque onTokenRefresh()appel, vous appelez à nouveau FirebaseInstanceID.getToken () , obtenez un nouveau jeton et envoyez-le au serveur (y compris probablement l'ancien jeton afin que votre serveur puisse le supprimer, en le remplaçant par le nouveau) .


2
J'ai implémenté FirebaseInstanceIdService, le problème est que onTokenRefresh () est appelé presque immédiatement après que l'utilisateur a installé l'application. J'ai besoin qu'il soit appelé après avoir effectué une connexion /
inscription

1
Donc, la suppression de FirebaseInstanceId actualisera le jeton, merci!
Louis CAD

après GCM vers FCM, FirebaseInstanceId.getInstance (). getToken (); renvoie toujours null. Toute solution?
Govinda Paliwal

@TareKhoury Vous pouvez appeler cette méthode partout où cela est nécessaire pour obtenir un jeton. FirebaseInstanceId.getInstance (). GetToken ();
sssvrock

@pRaNaY en cas de mise à jour de l'application cliente sera onTokenRefresh()appelé?
Piyush Kukadiya

2

Je maintiens un indicateur dans la préférence partagée qui indique si le jeton gcm est envoyé au serveur ou non. Dans l'écran Splash chaque fois que j'appelle une méthode sendDevicetokenToServer. Cette méthode vérifie si l'ID utilisateur n'est pas vide et l'état d'envoi de gcm, 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");
}

2

Les gars, il a une solution très simple

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

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

Au lieu de supprimer l'ID d'instance, supprimez uniquement le jeton:

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

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

1

Ceci est dans RxJava2 dans le scénario lorsqu'un utilisateur se déconnecte de votre application et que d'autres utilisateurs se connectent (même application) login api)

Single.fromCallable(() -> FirebaseInstanceId.getInstance().getToken())
            .flatMap( token -> Retrofit.login(userName,password,token))
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(simple -> {
                if(simple.isSuccess){
                    loginedSuccessfully();
                }
            }, throwable -> Utils.longToast(context, throwable.getLocalizedMessage()));

S'identifier

@FormUrlEncoded
@POST(Site.LOGIN)
Single<ResponseSimple> login(@Field("username") String username,
                         @Field("password") String pass,
                         @Field("token") String token

);

0

Cette réponse ne détruit pas l'identifiant de l'instance, mais peut en obtenir le courant. Il stocke également une actualisée 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 dans laquelle vous souhaitez 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 supérieur onCreatedu service n'est pas appelé lors du démarrage automatique ( source ). Au lieu de cela, il onStartCommandest remplacé et utilisé. Mais dans FirebaseInstanceIdService, il est déclaré comme final et ne peut pas être remplacé. Cependant, lorsque nous démarrons le service en utilisant startService (), si le service est déjà en cours d'exécution, son instance d'origine est utilisée (ce qui est bien). Notre onCreate () (défini ci-dessus) a également été appelé !.

Utilisez ceci au début de MainActivity ou à tout 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())

Notez que vous pouvez encore améliorer cela en essayant de déplacer le code startservice () vers la méthode getFirebaseInstanceId.


Si vous réinitialisez l'application / exécutez la première fois, l'actualisation du jeton prend un certain temps. Ainsi, vous obtiendrez la chaîne "par défaut" pendant une minute ou deux.
Varun Garg

0
    [Service]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
class MyFirebaseIIDService: FirebaseInstanceIdService
{
    const string TAG = "MyFirebaseIIDService";
    NotificationHub hub;

    public override void OnTokenRefresh()
    {
        var refreshedToken = FirebaseInstanceId.Instance.Token;
        Log.Debug(TAG, "FCM token: " + refreshedToken);
        SendRegistrationToServer(refreshedToken);
    }

    void SendRegistrationToServer(string token)
    {
        // Register with Notification Hubs
        hub = new NotificationHub(Constants.NotificationHubName,
                                    Constants.ListenConnectionString, this);
        Employee employee = JsonConvert.DeserializeObject<Employee>(Settings.CurrentUser);
        //if user is not logged in 
        if (employee != null)
        {
            var tags = new List<string>() { employee.Email};
            var regID = hub.Register(token, tags.ToArray()).RegistrationId;

            Log.Debug(TAG, $"Successful registration of ID {regID}");
        }
        else
        {
            FirebaseInstanceId.GetInstance(Firebase.FirebaseApp.Instance).DeleteInstanceId();
            hub.Unregister();
        }
    }
}

0

FirebaseInstanceIdService

Cette classe est obsolète. En faveur de la substitution de onNewToken dans FirebaseMessagingService. Une fois que cela a été mis en œuvre, ce service peut être supprimé en toute sécurité.

La nouvelle façon de procéder serait de remplacer la onNewTokenméthode deFirebaseMessagingService

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);
    }
} 

N'oubliez pas non plus d'ajouter le service dans le fichier Manifest.xml

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

0

Comment je mets à jour mon appareil

Tout d'abord, lorsque je me connecte, j'envoie le premier jeton d'appareil sous la collection d'utilisateurs et l'utilisateur actuellement connecté.

Après cela, je remplace simplement onNewToken(token:String)dans my FirebaseMessagingService()et je mets simplement à jour cette valeur si un nouveau jeton est généré pour cet utilisateur

class MyFirebaseMessagingService: FirebaseMessagingService() {
    override fun onMessageReceived(p0: RemoteMessage) {
        super.onMessageReceived(p0)
    }

    override fun onNewToken(token: String) {
    super.onNewToken(token)
    val currentUser= FirebaseAuth.getInstance().currentUser?.uid
    if(currentUser != null){
        FirebaseFirestore.getInstance().collection("user").document(currentUser).update("deviceToken",token)
    }
 }
} 

Chaque fois que votre application s'ouvre, elle recherchera un nouveau jeton, si l'utilisateur n'est pas encore connecté, elle ne mettra pas à jour le jeton, si l'utilisateur est déjà connecté, vous pouvez vérifier un newToken

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.