Firebase FCM erzwingt den Aufruf von onTokenRefresh ()


111

Ich migriere meine App von GCM zu FCM.

Wenn ein neuer Benutzer meine App installiert, onTokenRefresh()wird die automatisch aufgerufen. Das Problem ist, dass der Benutzer noch nicht angemeldet ist (keine Benutzer-ID).

Wie kann ich das auslösen, onTokenRefresh()nachdem der Benutzer angemeldet ist?


1
Eine sehr ähnliche Frage wurde bereits im folgenden Link gestellt. Überprüfen Sie, ob die Antwort für Sie nützlich ist: stackoverflow.com/questions/37517254/…
Diego Giorgini

Antworten:


172

Die onTokenRefresh()Methode wird immer dann aufgerufen, wenn ein neues Token generiert wird. Bei der Installation der App wird sie sofort generiert (wie Sie festgestellt haben). Es wird auch aufgerufen, wenn sich das Token geändert hat.

Nach dem FirebaseCloudMessagingLeitfaden:

Sie können Benachrichtigungen auf ein einzelnes bestimmtes Gerät ausrichten. Beim ersten Start Ihrer App generiert das FCM SDK ein Registrierungstoken für die Client-App-Instanz.

Bildschirmfoto

Quelllink: https://firebase.google.com/docs/notifications/android/console-device#access_the_registration_token

Dies bedeutet, dass die Token-Registrierung pro App erfolgt. Es hört sich so an, als ob Sie das Token verwenden möchten, nachdem ein Benutzer angemeldet ist. Ich würde vorschlagen, dass Sie das Token in der onTokenRefresh()Methode im internen Speicher oder in gemeinsam genutzten Einstellungen speichern. Rufen Sie dann das Token aus dem Speicher ab, nachdem sich ein Benutzer angemeldet hat, und registrieren Sie das Token nach Bedarf bei Ihrem Server.

Wenn Sie das manuell erzwingen möchten onTokenRefresh(), können Sie einen IntentService erstellen und die Token-Instanz löschen. Wenn Sie dann getToken aufrufen, wird die onTokenRefresh()Methode erneut aufgerufen.

Beispielcode:

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

BEARBEITEN

FirebaseInstanceIdService

öffentliche Klasse FirebaseInstanceIdService erweitert Service

Diese Klasse ist veraltet. Für das Überschreiben von onNewToken in FirebaseMessagingService. Sobald dies implementiert wurde, kann dieser Dienst sicher entfernt werden.

onTokenRefresh () ist veraltet . Verwenden Sie 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
Selbst wenn onTokenRefresh () aufgerufen wird, bevor der Benutzer angemeldet ist, können Sie das Token bei der Anmeldung des Benutzers mit FirebaseInstanceId.getInstance (). GetToken () abrufen und an den Server senden, anstatt es lokal zu speichern für die Registrierung. (es sei denn, Sie möchten es lokal speichern, um ein altes Token von Ihrem Server zu löschen)
Geekoraul

10
FireBase ist clever und ruft die Methode onTokenRefresh () nur dann auf, wenn kein Token vorhanden ist (es wird gelöscht oder zum ersten Mal aufgerufen) oder etwas anderes passiert und das Token geändert wurde. Wenn jemand onTokenRefresh aufrufen möchte, kann er das Token löschen und dann FirebaseInstanceId.getInstance (). GetToken () aufrufen. Operation FirebaseInstanceId.getInstance (). DeleteInstanceId () muss sich in AsyncTask oder einem neuen Thread befinden, es konnte sich nicht auf dem MainThread befinden !!!
Stoycho Andreev

3
Warum nicht einfach FirebaseInstanceId.getToken aufrufen?
Esong

1
Nun, es hat perfekt funktioniert, wenn Sie IntentService aufgerufen haben, und ich denke, Sie müssen kein Token in den Einstellungen speichern. Da sich der Wert erst ändert, wenn FirebaseInstanceId.getInstance (). DeleteInstanceId (); wird genannt. Ein bisschen hat mir den Tag gerettet. :)
Detoxic-Soul

5
Warum das Token in freigegebenen Einstellungen speichern - wenn Sie FirebaseInstanceId.getInstance (). GetToken () jederzeit aufrufen können, um seinen Wert abzurufen ?
Alexander Farber

18

Versuchen Sie zu implementieren FirebaseInstanceIdService, um ein Aktualisierungstoken zu erhalten.

Greifen Sie auf das Registrierungstoken zu:

Sie können auf den Wert des Tokens zugreifen, indem Sie FirebaseInstanceIdService erweitern . Stellen Sie sicher, dass Sie den Dienst zu Ihrem Manifest hinzugefügt haben , rufen Sie dann getTokenim Kontext von auf onTokenRefreshund protokollieren Sie den Wert wie folgt:

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

Vollständiger Code:

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

Siehe meine Antwort hier .

EDITS:

Sie sollten einen FirebaseInstanceIdService nicht selbst starten .

Es wird aufgerufen, wenn das System feststellt, dass die Token aktualisiert werden müssen. Die Anwendung sollte getToken () aufrufen und die Token an alle Anwendungsserver senden.

Dies wird nicht sehr häufig aufgerufen. Es wird für die Schlüsselrotation und zur Behandlung von Änderungen der Instanz-ID benötigt, und zwar aufgrund von:

  • App löscht Instanz-ID
  • App wird auf einem neuen Gerätebenutzer wiederhergestellt
  • deinstalliert / installiert die App neu
  • Benutzer löscht App-Daten

Das System drosselt das Aktualisierungsereignis auf allen Geräten, um eine Überlastung der Anwendungsserver mit Token-Updates zu vermeiden.

Versuchen Sie es unten :

Sie würden FirebaseInstanceID.getToken () irgendwo außerhalb Ihres Hauptthreads aufrufen (ob es sich um einen Dienst, AsyncTask usw. handelt), das zurückgegebene Token lokal speichern und an Ihren Server senden. Wenn onTokenRefresh()Sie dann aufgerufen werden, rufen Sie FirebaseInstanceID.getToken () erneut auf, erhalten ein neues Token und senden es an den Server (wahrscheinlich auch das alte Token, damit Ihr Server es entfernen und durch das neue ersetzen kann). .


2
Ich habe FirebaseInstanceIdService implementiert. Das Problem ist, dass onTokenRefresh () fast unmittelbar nach der Installation der App durch den Benutzer aufgerufen wird. Ich brauche es, um nach einem Login / Signup
TareK Khoury

1
Wenn Sie also die FirebaseInstanceId löschen, wird das Token aktualisiert, danke!
Louis CAD

nach GCM zu FCM FirebaseInstanceId.getInstance (). getToken (); Geben Sie immer null zurück. Irgendeine Lösung?
Govinda Paliwal

@TareKhoury Sie können diese Methode aufrufen, wo immer dies erforderlich ist, um ein Token zu erhalten. FirebaseInstanceId.getInstance (). GetToken ();
Sssvrock

@pRaNaY im Falle eines Client-App-Updates wird onTokenRefresh()aufgerufen?
Piyush Kukadiya

2

Ich behalte ein Flag in der freigegebenen Voreinstellung bei, das angibt, ob ein gcm-Token an den Server gesendet wurde oder nicht. Im Begrüßungsbildschirm jedes Mal, wenn ich eine Methode sendDevicetokenToServer aufrufe. Diese Methode prüft, ob die Benutzer-ID nicht leer ist und der gcm-Sendestatus das Token an den Server sendet.

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

}

}}

In der MyFirebaseInstanceIDService-Klasse

    @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

Leute, es hat eine sehr einfache Lösung

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

Hinweis: Wenn Ihre App Token verwendet hat, die von deleteInstanceID gelöscht wurden, muss Ihre App Ersatz-Token generieren.

Anstatt die Instanz-ID zu löschen, löschen Sie nur das Token:

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

2
Hat bei mir nicht funktioniert. Nach dem Aufruf von deleteToken () gibt getToken () dasselbe Token wie zuvor zurück und onTokenRefresh wurde nicht aufgerufen.
Lera

1

Dies ist in RxJava2 in einem Szenario der Fall, in dem sich ein Benutzer von Ihrer App abmeldet und andere Benutzer sich anmelden (gleiche App). Zum erneuten Starten und Anrufen der Anmeldung (Wenn das Gerät des Benutzers zum Zeitpunkt des Aktivitätsstarts keine frühere Internetverbindung hatte und wir ein Token senden müssen 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()));

Anmeldung

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

);

0

Diese Antwort zerstört nicht die Instanz-ID, sondern kann die aktuelle ID abrufen. Es wird auch eine aktualisierte Datei in den freigegebenen Einstellungen gespeichert.

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 (jede Klasse, in der Sie Einstellungen festlegen / abrufen möchten)

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

}}

Der Dienst für Android 2.0 und höher onCreatewird beim automatischen Start nicht aufgerufen ( Quelle ). Stattdessen onStartCommandwird überschrieben und verwendet. Im tatsächlichen FirebaseInstanceIdService wird es jedoch als endgültig deklariert und kann nicht überschrieben werden. Wenn wir den Dienst jedoch mit startService () starten und der Dienst bereits ausgeführt wird, wird seine ursprüngliche Instanz verwendet (was gut ist). Unser onCreate () (oben definiert) wurde ebenfalls aufgerufen!.

Verwenden Sie dies zu Beginn von MainActivity oder an dem Punkt, an dem Sie glauben, dass Sie eine Instanz-ID benötigen.

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

Und schlussendlich,

Utility.getFirebaseInstanceId(getApplicationContext())

Beachten Sie, dass Sie dies weiter verbessern können, indem Sie versuchen, den Code von serviceservice () in die Methode getFirebaseInstanceId zu verschieben.


Wenn Sie die App zurücksetzen / zum ersten Mal ausführen, dauert es einige Zeit, bis das Token aktualisiert ist. Sie erhalten also für ein oder zwei Minuten die Zeichenfolge "Standard".
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

Diese Klasse ist veraltet. Für das Überschreiben von onNewToken in FirebaseMessagingService. Sobald dies implementiert wurde, kann dieser Dienst sicher entfernt werden.

Der neue Weg, dies zu tun, wäre, die onNewTokenMethode von zu überschreibenFirebaseMessagingService

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

Vergessen Sie auch nicht, den Dienst in die Manifest.xml aufzunehmen

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

0

Wie aktualisiere ich mein deviceToken?

Wenn ich mich anmelde, sende ich zuerst das erste Geräte-Token unter der Benutzersammlung und den aktuell angemeldeten Benutzer.

Danach überschreibe ich einfach onNewToken(token:String)meine FirebaseMessagingService()und aktualisiere diesen Wert, wenn für diesen Benutzer ein neues Token generiert wird

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

Jedes Mal, wenn Ihre App geöffnet wird, wird nach einem neuen Token gesucht. Wenn der Benutzer noch nicht angemeldet ist, wird das Token nicht aktualisiert. Wenn der Benutzer bereits angemeldet ist, können Sie nach einem Token suchen newToken

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.