Firebase FCM принудительно вызывает onTokenRefresh ()


111

Я переношу свое приложение с GCM на FCM.

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

Как я могу активировать onTokenRefresh()после входа пользователя в систему?


1
Очень похожий вопрос уже задавался по следующей ссылке. Проверьте, полезен ли вам ответ: stackoverflow.com/questions/37517254/…
Диего Джорджини,

Ответы:


172

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

По словам FirebaseCloudMessagingгида:

Вы можете направлять уведомления на одно конкретное устройство. При первоначальном запуске вашего приложения FCM SDK генерирует регистрационный токен для экземпляра клиентского приложения.

Скриншот

Ссылка на источник: 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);
    }
}

РЕДАКТИРОВАТЬ

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 умен, и он будет вызывать метод onTokenRefresh (), только если токена нет (он удален или вызывается в первый раз) или что-то еще происходит, и токен был изменен. Если кто-то хочет вызвать onTokenRefresh, может удалить токен, а затем вызвать FirebaseInstanceId.getInstance (). GetToken (). Операция FirebaseInstanceId.getInstance (). DeleteInstanceId () должна быть в AsyncTask или новом потоке, она не может быть в MainThread !!!
Стойчо Андреев

3
Почему бы просто не вызвать FirebaseInstanceId.getToken?
эсонг

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 () вызывается почти сразу после того, как пользователь устанавливает приложение.
Мне

1
Итак, удаление FirebaseInstanceId обновит токен, спасибо!
Louis CAD

после GCM в FCM, FirebaseInstanceId.getInstance (). getToken (); всегда возвращать null. Любое решение?
Говинда Паливал

@TareKhoury Вы можете вызывать этот метод везде, где требуется для получения токена. FirebaseInstanceId.getInstance (). GetToken ();
sssvrock

@pRaNaY в случае обновления клиентского приложения будет onTokenRefresh()вызываться?
Пиюш Кукадия

2

Я поддерживаю один флаг в общих настройках, который указывает, отправлен ли токен gcm на сервер или нет. На экране-заставке я каждый раз вызываю один метод 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/instance-id/guides/android-implementation#generate_a_token

Примечание. Если ваше приложение использовало токены, которые были удалены с помощью deleteInstanceID, вашему приложению потребуется сгенерировать заменяющие токены.

Вместо удаления идентификатора экземпляра удалите только токен:

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 он объявлен как final и не может быть отменен. Однако, когда мы запускаем службу с помощью 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.


Если вы сбросите app / run в первый раз, потребуется некоторое время, чтобы обновить токен. Таким образом, вы получите строку «default» на минуту или две.
Варун Гарг

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

Как я обновляю свой deviceToken

Сначала, когда я вхожу в систему, я отправляю первый токен устройства в коллекции пользователей и текущего пользователя, вошедшего в систему.

После этого я просто переопределяю 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.