FCbase Firebase змушує OnTokenRefresh () викликати


111

Я переношу свій додаток із GCM на FCM.

Коли новий користувач встановить мій додаток, onTokenRefresh()автоматично викликається. Проблема полягає в тому, що користувач ще не зареєстрований (Немає ідентифікатора користувача).

Як я можу запустити onTokenRefresh()після входу користувача?


1
Дуже подібне запитання вже було задано у наступному посиланні. Перевірте, чи корисна вам відповідь: stackoverflow.com/questions/37517254/…
Дієго Джорджіні

Відповіді:


172

onTokenRefresh()Метод буде викликатися щоразу , коли новий маркер генерується. Після встановлення програми він буде генерований негайно (як ви виявили). Він також буде викликаний, коли маркер змінився.

Згідно з FirebaseCloudMessagingпосібником:

Ви можете орієнтувати сповіщення на один конкретний пристрій. При первинному запуску програми, SDK FCM генерує марку реєстрації для екземпляра клієнтського додатка.

Знімок екрана

Посилання на джерело: https://firebase.google.com/docs/notifications/android/console-device#access_the_registration_token

Це означає, що реєстрація лексеми здійснюється за програмою. Здається, ви хочете використовувати маркер після входу користувача. Я б запропонував вам зберегти маркер onTokenRefresh()методу для внутрішнього зберігання або загальних налаштувань. Потім вийміть маркер із сховища після входу користувача та зареєструйте маркер на своєму сервері за потребою.

Якщо ви хочете вручну примусити onTokenRefresh(), ви можете створити IntentService та видалити екземпляр маркера. Потім, коли ви телефонуєте getToken, onTokenRefresh()метод буде викликаний знову.

Приклад коду:

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

EDIT

FirebaseInstanceIdService

публічний клас FirebaseInstanceIdService розширює службу

Цей клас застарілий. На користь переосмислення OnNewToken у FirebaseMessagingService. Після того, як це було здійснено, цю послугу можна буде безпечно видалити.

onTokenRefresh () застарілий . Використання onNewToken()в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);
    }
} 

27
Навіть якщо виклик onTokenRefresh () перед тим, як користувач увійшов до системи, замість того, щоб виконувати роботу зі зберігання на локальному рівні, коли користувач входить у систему, ви можете отримати маркер за допомогою FirebaseInstanceId.getInstance (). GetToken () та надіслати його на сервер для реєстрації. (якщо ви не хочете зберігати його локально для видалення старого маркера зі свого сервера)
geekoraul

10
FireBase розумний, і він зателефонує за методомTokenRefresh (), лише якщо немає маркера (він видаляється або він викликається вперше) або трапляється щось інше і маркер змінено. Якщо хтось хоче зателефонувати на onTokenRefresh, він може видалити маркер, а потім зателефонувати FirebaseInstanceId.getInstance (). GetToken (). Операція FirebaseInstanceId.getInstance (). DeleteInstanceId () повинна бути в AsyncTask або новій темі, вона не могла бути на MainThread !!!
Стойчо Андрєєв

3
Чому б просто не зателефонувати FirebaseInstanceId.getToken?
esong

1
добре, працював ідеально під час дзвінка в IntentService, і не потрібно зберігати маркер у префіксах, я думаю. Оскільки значення не змінюється, поки FirebaseInstanceId.getInstance (). DeleteInstanceId (); це називається. Ніби врятував мені день. :)
Detoxic-Soul

5
Навіщо зберігати маркер у спільних налаштуваннях - якщо ви можете в будь-який час зателефонувати FirebaseInstanceId.getInstance (). GetToken (), щоб отримати його значення?
Олександр Фарбер

18

Спробуйте здійснити, FirebaseInstanceIdServiceщоб отримати маркер оновлення.

Доступ до маркера реєстрації:

Ви можете отримати доступ до значення маркера, розширивши FirebaseInstanceIdService . Переконайтеся, що ви додали послугу до свого маніфесту , а потім зателефонуйте getTokenв контексті onTokenRefreshта запишіть значення, як показано:

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

Повний код:

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

Дивіться мою відповідь тут .

ЗМІНИ:

Ви не повинні самостійно запускати FirebaseInstanceIdService .

Викликається, коли система визначить, що маркери потрібно оновити. Додаток повинен викликати getToken () та надсилати жетони на всі сервери додатків.

Це не буде називатися дуже часто, воно потрібне для обертання ключів та для обробки змін Ідентифікатора екземпляра через:

  • Додаток видаляє ідентифікатор екземпляра
  • Додаток відновлюється на новому користувачі пристрою
  • видалити / перевстановити додаток
  • Користувач очищає дані додатків

Система зменшить події оновлення на всіх пристроях, щоб уникнути перевантаження серверів додатків оновленнями токенів.

Спробуйте нижче :

ви б зателефонували FirebaseInstanceID.getToken () з будь-якого місця у ваш основний потік (будь то служба, AsyncTask тощо), зберігайте повернутий маркер локально та відправляйте його на ваш сервер. Тоді щоразу, коли onTokenRefresh()буде викликано, ви знову зателефонуєте FirebaseInstanceID.getToken () , отримаєте новий маркер і надішлете його на сервер (можливо, включаючи і старий маркер, щоб ваш сервер міг його видалити, замінивши його новим) .


2
Я реалізував FirebaseInstanceIdService, проблема в OnTokenRefresh () викликається майже відразу після встановлення користувачем програми. мені потрібно, щоб його викликали після входу / реєстрації
TareK Khoury

1
Тож видалення FirebaseInstanceId оновить маркер, дякую!
Луї CAD

після GCM до FCM, FirebaseInstanceId.getInstance (). getToken (); завжди повертати null. Будь-яке рішення?
Govinda Paliwal

@TareKhoury Ви можете викликати цей метод там, де коли-небудь потрібно отримати маркер. FirebaseInstanceId.getInstance (). GetToken ();
sssvrock

@pRaNaY у випадку оновлення клієнтського додатка onTokenRefresh()?
Піюш Кукадія

2

Я підтримую один прапор у спільному префіксі, який вказує, чи маркер gcm надсилається на сервер чи ні. На екрані Splash кожен раз, коли я викликаю один метод sendDevicetokenToServer. Цей метод перевіряє, чи ідентифікатор користувача не порожній, і gcm статус надсилання, а потім надсилає маркер на сервер.

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

}

}

У класі 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

Хлопці, це дуже просте рішення

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

Примітка. Якщо ваш додаток використовував жетони, які були видалені deleteInstanceID, вашому додатку потрібно буде генерувати замінні маркери.

Замість видалення Id екземпляра видаліть лише маркер:

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

2
Не працювало для мене. Після виклику deleteToken (), getToken () повертає той же маркер, що і раніше, і onTokenRefresh не викликався.
Лера

1

Це в RxJava2 в сценарії, коли один користувач виходить із вашого додатка та інші користувачі ввійшли (той самий додаток), щоб регенерувати та входити в дзвінки (Якщо на пристрої користувача не було підключення до Інтернету раніше під час запуску діяльності, і нам потрібно надіслати маркер у 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()));

Вхід

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

);

0

Ця відповідь не знищує ідентифікатор екземпляра, натомість він може отримати поточний. Він також зберігає оновлений в спільних налаштуваннях.

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 (будь-який клас, де потрібно встановити / отримати налаштування)

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 (розширює 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);
     ....

}

onCreateСервіс Android 2.0 і вище не запускається при автоматичному запуску ( джерело ). Натомість onStartCommandпереосмислюється та використовується. Але в фактичному FirebaseInstanceIdService він оголошується як остаточний і його не можна перекрити. Однак, коли ми запускаємо сервіс за допомогою startService (), якщо служба вже запущена, використовується її оригінальний примірник (що добре). Наш onCreate () (визначений вище) також отримав посилання!

Використовуйте це на початку MainActivity або в той момент, коли вам здається, що вам потрібен ідентифікатор екземпляра.

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

І, нарешті,

Utility.getFirebaseInstanceId(getApplicationContext())

Зверніть увагу , ви можете покращити це, намагаючись перемістити код startservice () на метод getFirebaseInstanceId.


Якщо ви скидаєте програму / запускаєте перший раз, для оновлення маркера знадобиться певний час. Таким чином, ви отримаєте рядок "за замовчуванням" протягом хвилини або двох.
Варун Гарг

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

Цей клас застарілий. На користь переосмислення OnNewToken у FirebaseMessagingService. Після того, як це було здійснено, цю послугу можна буде безпечно видалити.

Новим способом зробити це було б замінення onNewTokenметоду зFirebaseMessagingService

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

Також не забудьте додати послугу в Manifest.xml

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

0

Як оновити свій пристрійToken

Перш за все, коли я входжу в систему, я надсилаю перший маркер пристрою під колекцію користувача та поточний користувач, який увійшов.

Після цього я просто переосмислюю onNewToken(token:String)своє FirebaseMessagingService()і просто оновлюю це значення, якщо для цього користувача генерується новий маркер

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

Кожен раз, коли ваше додаток відкриється, він перевірятиме новий маркер, якщо користувач ще не ввійшов, він не оновлює маркер, якщо користувач уже ввійшов у систему, ви можете перевірити наявність newToken

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.