Firebase FCM wymusza wywołanie onTokenRefresh ()


111

Migracja aplikacji z GCM do FCM.

Gdy nowy użytkownik instaluje moją aplikację, onTokenRefresh()jest automatycznie wywoływana. Problem polega na tym, że użytkownik nie jest jeszcze zalogowany (brak identyfikatora użytkownika).

Jak mogę wywołać onTokenRefresh()po zalogowaniu się użytkownika?


1
Bardzo podobne pytanie zostało już zadane w poniższym linku. Sprawdź, czy odpowiedź Ci się przyda: stackoverflow.com/questions/37517254/…
Diego Giorgini

Odpowiedzi:


172

Plik onTokenRefresh()Metoda zostanie wywołana, kiedy nowy znak jest generowany. Po zainstalowaniu aplikacji zostanie ona natychmiast wygenerowana (tak jak znalazłeś). Zostanie również wywołany, gdy token się zmieni.

Według FirebaseCloudMessagingprzewodnika:

Możesz kierować powiadomienia na jedno, określone urządzenie. Podczas początkowego uruchamiania aplikacji FCM SDK generuje token rejestracji dla wystąpienia aplikacji klienta.

Zrzut ekranu

Link do źródła: https://firebase.google.com/docs/notifications/android/console-device#access_the_registration_token

Oznacza to, że rejestracja tokena dotyczy jednej aplikacji. Wygląda na to, że chciałbyś wykorzystać token po zalogowaniu się użytkownika. Sugeruję, abyś zapisał token wonTokenRefresh() metodzie do pamięci wewnętrznej lub wspólnych preferencji. Następnie pobierz token z magazynu po zalogowaniu się użytkownika i zarejestruj go na serwerze w razie potrzeby.

Jeśli chcesz ręcznie wymusić onTokenRefresh(), możesz utworzyć IntentService i usunąć instancję tokenu. Następnie, gdy wywołasz getToken, onTokenRefresh()metoda zostanie wywołana ponownie.

Przykładowy kod:

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

EDYTOWAĆ

FirebaseInstanceIdService

public class FirebaseInstanceIdService rozszerza usługę

Ta klasa jest przestarzała. Na korzyść zastąpienia onNewToken w FirebaseMessagingService. Po wdrożeniu tę usługę można bezpiecznie usunąć.

onTokenRefresh () jest przestarzały . Użyj onNewToken()wMyFirebaseMessagingService

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
Nawet jeśli onTokenRefresh () jest wywoływany przed zalogowaniem użytkownika, zamiast wykonywać pracę polegającą na przechowywaniu go lokalnie, po zalogowaniu się użytkownika można pobrać token za pomocą FirebaseInstanceId.getInstance (). GetToken () i wysłać go na serwer Do rejestracji. (chyba że chcesz przechowywać go lokalnie w celu usunięcia starego tokena z serwera)
geekoraul

10
FireBase jest sprytny i wywoła metodę onTokenRefresh () tylko wtedy, gdy nie ma tokena (jest usuwany lub jest wywoływany po raz pierwszy) lub dzieje się coś innego i token został zmieniony. Jeśli ktoś chce wywołać onTokenRefresh, może usunąć token, a następnie wywołać FirebaseInstanceId.getInstance (). GetToken (). Operacja FirebaseInstanceId.getInstance (). DeleteInstanceId () musi być w AsyncTask lub w nowym wątku, nie może być w głównym wątku !!!
Stoycho Andreev

3
Dlaczego po prostu nie wywołać FirebaseInstanceId.getToken?
esong

1
cóż, działało idealnie podczas wywoływania w IntentService i chyba nie ma potrzeby zapisywania tokena w prefs. Ponieważ wartość nie zmienia się do czasu FirebaseInstanceId.getInstance (). DeleteInstanceId (); jest nazywany. Kinda uratowała mi dzień. :)
Detoxic-Soul

5
Po co zapisywać token we wspólnych preferencjach - jeśli w dowolnym momencie możesz wywołać FirebaseInstanceId.getInstance (). GetToken (), aby uzyskać jego wartość?
Alexander Farber

18

Spróbuj zaimplementować, FirebaseInstanceIdServiceaby uzyskać odświeżający token.

Uzyskaj dostęp do tokena rejestracyjnego:

Możesz uzyskać dostęp do wartości tokena, rozszerzając FirebaseInstanceIdService . Upewnij się, że dodano usługę do manifestu , a następnie wywołaj getTokenw kontekście onTokenRefreshi zarejestruj wartość, jak pokazano:

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

Pełny kod:

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

Zobacz moją odpowiedź tutaj .

EDYCJE:

Nie powinieneś samodzielnie uruchamiać usługi FirebaseInstanceIdService .

Zostanie wywołany, gdy system ustali, że tokeny wymagają odświeżenia. Aplikacja powinna wywołać metodę getToken () i wysłać tokeny do wszystkich serwerów aplikacji.

Nie będzie to wywoływane zbyt często, jest potrzebne do rotacji kluczy i obsługi zmian identyfikatora instancji z powodu:

  • Aplikacja usuwa identyfikator instancji
  • Aplikacja zostanie przywrócona na nowym urządzeniu użytkownika
  • odinstalowuje / ponownie instaluje aplikację
  • Użytkownik czyści dane aplikacji

System ograniczy zdarzenie odświeżania na wszystkich urządzeniach, aby uniknąć przeciążenia serwerów aplikacji aktualizacjami tokenów.

Spróbuj poniżej :

że zadzwonisz FirebaseInstanceID.getToken () gdziekolwiek poza głównym wątku (czy jest to usługa, AsyncTask itp), przechowywać zwrócony tokena lokalnie i przesłać go na serwer. Następnie przy każdym onTokenRefresh()wywołaniu należy ponownie wywołać FirebaseInstanceID.getToken () , pobrać nowy token i wysłać go na serwer (prawdopodobnie łącznie ze starym tokenem, aby serwer mógł go usunąć, zastępując go nowym) .


2
Zaimplementowałem FirebaseInstanceIdService, problem polega na tym, że onTokenRefresh () jest wywoływany prawie natychmiast po zainstalowaniu aplikacji przez użytkownika. Muszę zadzwonić po wykonaniu logowania /
rejestracji

1
Dlatego usunięcie FirebaseInstanceId spowoduje odświeżenie tokena, dzięki!
Louis CAD

po GCM do FCM, FirebaseInstanceId.getInstance (). getToken (); zawsze zwraca null. Jakieś rozwiązanie?
Govinda Paliwal

@TareKhoury Możesz wywołać tę metodę wszędzie tam, gdzie jest to wymagane, aby uzyskać token. FirebaseInstanceId.getInstance (). GetToken ();
sssvrock

@pRaNaY w przypadku aktualizacji aplikacji klienta zostanie onTokenRefresh()wywołana?
Piyush Kukadiya

2

Utrzymuję jedną flagę we wspólnym pref, która wskazuje, czy token gcm został wysłany do serwera, czy nie. Na ekranie powitalnym za każdym razem, gdy wywołuję jedną metodę sendDevicetokenToServer. Ta metoda sprawdza, czy identyfikator użytkownika nie jest pusty, a status wysyłania gcm, a następnie wysyła token do serwera.

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

}

}

W klasie 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

Chłopaki, ma bardzo proste rozwiązanie

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

Uwaga: jeśli Twoja aplikacja używała tokenów, które zostały usunięte przez deleteInstanceID, aplikacja będzie musiała wygenerować tokeny zastępcze.

Zamiast usuwać identyfikator instancji, usuń tylko token:

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

2
Nie działa dla mnie. Po wywołaniu funkcji deleteToken (), getToken () zwraca ten sam token, co wcześniej, a onTokenRefresh nie został wywołany.
Lera

1

Jest to w RxJava2 w scenariuszu, w którym jeden użytkownik wylogowuje się z aplikacji, a inni użytkownicy logują się (ta sama aplikacja). 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()));

Zaloguj sie

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

);

0

Ta odpowiedź nie niszczy identyfikatora instancji, zamiast tego może pobrać bieżący. Przechowuje również odświeżoną wersję w preferencjach wspólnych.

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 (dowolna klasa, w której chcesz ustawić / pobrać preferencje)

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

}

onCreateUsługa Android 2.0 lub nowsza nie jest wywoływana podczas automatycznego uruchamiania ( źródło ). Zamiast tego onStartCommandjest zastępowany i używany. Ale w rzeczywistej usłudze FirebaseInstanceIdService jest zadeklarowany jako ostateczny i nie można go zastąpić. Jednak gdy uruchamiamy usługę za pomocą startService (), jeśli usługa już działa, używana jest jej oryginalna instancja (co jest dobre). Nasza onCreate () (zdefiniowana powyżej) również została wywołana !.

Użyj tego na początku MainActivity lub w dowolnym momencie, gdy potrzebujesz identyfikatora instancji.

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

I w końcu,

Utility.getFirebaseInstanceId(getApplicationContext())

Zauważ , że możesz to dodatkowo ulepszyć, próbując przenieść kod startedervice () do metody getFirebaseInstanceId.


Jeśli zresetujesz aplikację / uruchomisz ją po raz pierwszy, odświeżenie tokena zajmie trochę czasu. Otrzymasz więc ciąg „default” na minutę lub dwie.
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

Ta klasa jest przestarzała. Na korzyść zastąpienia onNewToken w FirebaseMessagingService. Po wdrożeniu tę usługę można bezpiecznie usunąć.

Nowym sposobem byłoby zastąpienie onNewTokenmetody fromFirebaseMessagingService

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

Nie zapomnij również dodać usługi w pliku Manifest.xml

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

0

Jak zaktualizować swój deviceToken

Najpierw, kiedy się loguję, wysyłam pierwszy token urządzenia w ramach kolekcji użytkowników i aktualnie zalogowanego użytkownika.

Następnie po prostu nadpisuję onNewToken(token:String)w mojej FirebaseMessagingService()i aktualizuję tę wartość, jeśli zostanie wygenerowany nowy token dla tego użytkownika

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

Za każdym razem, gdy Twoja aplikacja się otworzy, będzie sprawdzać nowy token, jeśli użytkownik nie jest jeszcze zalogowany, nie zaktualizuje tokena, jeśli użytkownik jest już zalogowany, możesz sprawdzić, czy newToken

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.