Firebase FCM forza la chiamata di onTokenRefresh ()


111

Sto migrando la mia app da GCM a FCM.

Quando un nuovo utente installa la mia app, onTokenRefresh()viene automaticamente chiamato. Il problema è che l'utente non è ancora loggato (nessun ID utente).

Come posso attivare il onTokenRefresh()dopo che l'utente ha effettuato l'accesso?


1
Una domanda molto simile è già stata posta nel seguente link. Controlla se la risposta ti è utile: stackoverflow.com/questions/37517254/…
Diego Giorgini

Risposte:


172

Il onTokenRefresh()metodo verrà chiamato ogni volta che viene generato un nuovo token. Al momento dell'installazione dell'app, verrà generata immediatamente (come hai riscontrato). Verrà chiamato anche quando il token sarà cambiato.

Secondo la FirebaseCloudMessagingguida:

Puoi indirizzare le notifiche a un singolo dispositivo specifico. All'avvio iniziale dell'app, l'SDK FCM genera un token di registrazione per l'istanza dell'app client.

Immagine dello schermo

Link di origine: https://firebase.google.com/docs/notifications/android/console-device#access_the_registration_token

Ciò significa che la registrazione del token è per app. Sembra che tu voglia utilizzare il token dopo che un utente ha effettuato l'accesso. Quello che suggerirei è di salvare il token nel onTokenRefresh()metodo nella memoria interna o nelle preferenze condivise. Quindi, recupera il token dalla memoria dopo che un utente ha effettuato l'accesso e registra il token con il tuo server secondo necessità.

Se desideri forzare manualmente il onTokenRefresh(), puoi creare un IntentService ed eliminare l'istanza del token. Quindi, quando chiami getToken, il onTokenRefresh()metodo verrà chiamato di nuovo.

Codice di esempio:

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

MODIFICARE

FirebaseInstanceIdService

la classe pubblica FirebaseInstanceIdService estende il servizio

Questa classe è deprecata. A favore dell'override di onNewToken in FirebaseMessagingService. Una volta implementato, questo servizio può essere rimosso in sicurezza.

onTokenRefresh () è deprecato . Usa onNewToken()inMyFirebaseMessagingService

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
Anche se onTokenRefresh () viene chiamato prima che l'utente abbia effettuato l'accesso, invece di eseguire il lavoro di memorizzarlo localmente, quando l'utente accede, puoi recuperare il token utilizzando FirebaseInstanceId.getInstance (). GetToken () e inviarlo al server per la registrazione. (a meno che tu non voglia memorizzarlo localmente per eliminare un vecchio token dal tuo server)
geekoraul

10
FireBase è intelligente e chiamerà il metodo onTokenRefresh (), solo se non c'è il token (viene cancellato o viene chiamato per la prima volta) o succede qualcos'altro e il token è stato modificato. Se qualcuno vuole chiamare onTokenRefresh può eliminare il token e quindi chiamare FirebaseInstanceId.getInstance (). GetToken (). L'operazione FirebaseInstanceId.getInstance (). DeleteInstanceId () deve essere in AsyncTask o in un nuovo thread, non potrebbe essere sul MainThread !!!
Stoycho Andreev

3
Perché non chiamare solo FirebaseInstanceId.getToken?
esong l'

1
beh, ha funzionato perfettamente quando si chiama IntentService e non è necessario salvare il token nelle preferenze, immagino. Poiché il valore non cambia fino a FirebaseInstanceId.getInstance (). DeleteInstanceId (); è chiamato. Mi ha salvato un po 'la giornata. :)
Detoxic-Soul

5
Perché salvare il token nelle preferenze condivise, se puoi chiamare FirebaseInstanceId.getInstance (). GetToken () in qualsiasi momento per ottenere il suo valore?
Alexander Farber

18

Prova a implementare FirebaseInstanceIdServiceper ottenere il token di aggiornamento.

Accedi al token di registrazione:

Puoi accedere al valore del token estendendo FirebaseInstanceIdService . Assicurati di aver aggiunto il servizio al tuo manifest , quindi chiama getTokennel contesto di onTokenRefreshe registra il valore come mostrato:

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

Codice completo:

   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.
    }
}

Vedi la mia risposta qui .

Modifiche:

Non dovresti avviare tu stesso un FirebaseInstanceIdService .

Viene chiamato quando il sistema determina che i token devono essere aggiornati. L'applicazione dovrebbe chiamare getToken () e inviare i token a tutti i server delle applicazioni.

Questo non verrà chiamato molto frequentemente, è necessario per la rotazione delle chiavi e per gestire le modifiche dell'ID istanza dovute a:

  • L'app elimina l'ID istanza
  • L'app viene ripristinata su un nuovo dispositivo Utente
  • disinstalla / reinstalla l'app
  • L'utente cancella i dati dell'app

Il sistema limiterà l'evento di aggiornamento su tutti i dispositivi per evitare di sovraccaricare i server delle applicazioni con gli aggiornamenti dei token.

Prova di seguito :

chiameresti FirebaseInstanceID.getToken () ovunque dal thread principale (che si tratti di un servizio, AsyncTask, ecc.), memorizzi il token restituito localmente e lo invii al tuo server. Quindi ogni volta che onTokenRefresh()viene chiamato, devi chiamare nuovamente FirebaseInstanceID.getToken () , ottenere un nuovo token e inviarlo al server (probabilmente includendo anche il vecchio token in modo che il tuo server possa rimuoverlo, sostituendolo con quello nuovo) .


2
Ho implementato FirebaseInstanceIdService, il problema è che onTokenRefresh () viene chiamato quasi immediatamente dopo che l'utente ha installato l'app. ho bisogno che venga chiamato dopo aver eseguito un accesso / registrazione
TareK Khoury

1
Quindi l'eliminazione di FirebaseInstanceId aggiornerà il token, grazie!
Louis CAD

dopo GCM in FCM, FirebaseInstanceId.getInstance (). getToken (); restituisce sempre null. Qualche soluzione?
Govinda Paliwal

@TareKhoury Puoi chiamare questo metodo dove richiesto per ottenere il token. . FirebaseInstanceId.getInstance () ControllaToken ();
sssvrock

@pRaNaY in caso di aggiornamento dell'app client verrà onTokenRefresh()chiamato?
Piyush Kukadiya

2

Sto mantenendo un flag nella pref condivisa che indica se il token gcm è stato inviato al server o meno. Nella schermata iniziale ogni volta che chiamo un metodo sendDevicetokenToServer. Questo metodo controlla se l'ID utente non è vuoto e lo stato di invio di gcm quindi invia il token al server.

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

}

}

Nella 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

Ragazzi, ha una soluzione molto semplice

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

Nota: se la tua app utilizzava token eliminati da deleteInstanceID, la tua app dovrà generare token sostitutivi.

Invece di eliminare l'ID istanza, elimina solo il token:

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

2
Non ha funzionato per me. Dopo aver chiamato deleteToken (), getToken () restituisce lo stesso token di prima e onTokenRefresh non è stato chiamato.
Lera

1

Questo è in RxJava2 nello scenario in cui un utente si disconnette dalla tua app e altri utenti accedono (stessa app) Per registrarsi e chiamare il login (se il dispositivo dell'utente non aveva una connessione a Internet in precedenza al momento dell'inizio dell'attività e dobbiamo inviare il token in 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()));

Accesso

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

);

0

Questa risposta non distrugge l'ID istanza, ma è in grado di ottenerne uno corrente. Memorizza anche quello aggiornato nelle preferenze condivise.

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 (qualsiasi classe in cui desideri impostare / ottenere preferenze)

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 (estende 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);
     ....

}

Il servizio Android 2.0 e versioni successive onCreatenon viene richiamato all'avvio automatico ( sorgente ). Invece onStartCommandè sovrascritto e utilizzato. Ma in FirebaseInstanceIdService effettivo è dichiarato come finale e non può essere sovrascritto. Tuttavia, quando avviamo il servizio utilizzando startService (), se il servizio è già in esecuzione, viene utilizzata la sua istanza originale (il che è positivo). Anche il nostro onCreate () (definito sopra) è stato invocato !.

Usalo all'inizio di MainActivity o in qualsiasi punto pensi di aver bisogno dell'ID istanza.

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

E infine,

Utility.getFirebaseInstanceId(getApplicationContext())

Nota , puoi migliorare ulteriormente questo aspetto provando a spostare il codice StartService () nel metodo getFirebaseInstanceId.


Se reimposti l'app / esegui la prima volta, è necessario un po 'di tempo per aggiornare il token. Quindi otterrai la stringa "default" per un minuto o due.
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

Questa classe è deprecata. A favore dell'override di onNewToken in FirebaseMessagingService. Una volta implementato, questo servizio può essere rimosso in sicurezza.

Il nuovo modo per farlo sarebbe sovrascrivere il onNewTokenmetodo daFirebaseMessagingService

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

Inoltre, non dimenticare di aggiungere il servizio nel Manifest.xml

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

0

Come aggiorno il mio dispositivo Token

Quando effettuo il login, invio il primo token del dispositivo nella raccolta utenti e l'utente attualmente connesso.

Dopodiché, sovrascrivo il onNewToken(token:String)mio FirebaseMessagingService()e aggiorno quel valore se viene generato un nuovo token per quell'utente

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

Ogni volta che la tua app si apre verificherà la presenza di un nuovo token, se l'utente non è ancora connesso non aggiornerà il token, se l'utente è già loggato puoi verificare newToken

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.