Firebase FCM force onTokenRefresh() будет называться

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

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

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

Ответ 1

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

Согласно руководству FirebaseCloudMessaging:

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

Screenshot

Source Link: 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);
    }
} 

Ответ 2

Попробуйте реализовать FirebaseInstanceIdService чтобы получить токен обновления.

Доступ к регистрационному токену:

Вы можете получить доступ к значению токена, расширив FirebaseInstanceIdService. Убедитесь, что вы добавили сервис в свой манифест, затем вызовите getToken в контексте onTokenRefresh и 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 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 servers.
        sendRegistrationToServer(refreshedToken);
    }
    // [END refresh_token]

    /**
     * Persist token to third-party servers.
     *
     * Modify this method to associate the user 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() и отправить токены всем серверам приложений.

Это не будет вызываться очень часто, это необходимо для поворота ключа и для обработки изменений ID экземпляра из-за:

  • Приложение удаляет идентификатор экземпляра
  • Приложение восстановлено на новом устройстве. Пользователь
  • удаляет/переустанавливает приложение
  • Пользователь очищает данные приложения

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

Попробуй ниже способ:

Вы должны вызывать FirebaseInstanceID.getToken() в любом месте вне основного потока (будь то служба, AsyncTask и т.д.), хранить возвращенный токен локально и отправлять его на свой сервер. Затем всякий раз, когда onTokenRefresh(), вы снова вызываете FirebaseInstanceID.getToken(), получаете новый токен и отправляете его на сервер (возможно, включая старый токен, чтобы ваш сервер мог удалить его, заменив его на новый).

Ответ 3

Я поддерживаю один флаг в shared pref, который указывает, отправлен ли 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");
}

Ответ 4

Ребята имеют очень простое решение

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

Ответ 5

Это происходит в 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

);

Ответ 6

Этот ответ не уничтожает идентификатор экземпляра, вместо этого он может получить текущий. Он также сохраняет обновленный в общих предпочтениях.

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

}

Android 2.0 и выше onCreate службы не вызывается при запуске автоматически (источник). Вместо этого 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())

Обратите внимание, что вы можете улучшить это, пытаясь переместить код startervice() в метод getFirebaseInstanceId.

Ответ 7

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

Ответ 8

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>