Firebase FCM força onTokenRefresh () a ser chamado

111

Estou migrando meu aplicativo do GCM para o FCM.

Quando um novo usuário instala meu aplicativo, o onTokenRefresh()está sendo chamado automaticamente. O problema é que o usuário ainda não está logado (sem identificação de usuário).

Como posso acionar o onTokenRefresh()depois que o usuário estiver logado?

TareK Khoury
fonte
1
Uma pergunta muito semelhante já foi feita no link a seguir. Verifique se a resposta é útil para você: stackoverflow.com/questions/37517254/…
Diego Giorgini

Respostas:

172

O onTokenRefresh()método será chamado sempre que um novo token for gerado. Após a instalação do aplicativo, ele será gerado imediatamente (como você descobriu ser o caso). Ele também será chamado quando o token for alterado.

De acordo com o FirebaseCloudMessagingguia:

Você pode direcionar notificações para um único dispositivo específico. Na inicialização inicial do seu aplicativo, o FCM SDK gera um token de registro para a instância do aplicativo cliente.

Captura de tela

Link da fonte: https://firebase.google.com/docs/notifications/android/console-device#access_the_registration_token

Isso significa que o registro do token é por aplicativo. Parece que você gostaria de utilizar o token depois que um usuário estiver conectado. O que eu sugiro é que você salve o token no onTokenRefresh()método para armazenamento interno ou preferências compartilhadas. Em seguida, recupere o token do armazenamento depois que um usuário fizer login e registre o token com seu servidor, conforme necessário.

Se desejar forçar manualmente o onTokenRefresh(), você pode criar um IntentService e excluir a instância do token. Então, quando você chamar getToken, o onTokenRefresh()método será chamado novamente.

Código de exemplo:

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

EDITAR

FirebaseInstanceIdService

public class FirebaseInstanceIdService estende serviço

Esta classe está obsoleta. A favor da substituição de onNewToken em FirebaseMessagingService. Uma vez que tenha sido implementado, este serviço pode ser removido com segurança.

onTokenRefresh () está obsoleto . Use onNewToken()emMyFirebaseMessagingService

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);
    }
} 
Principe
fonte
27
Mesmo se onTokenRefresh () for chamado antes de o usuário efetuar login, em vez de fazer o trabalho de armazená-lo localmente, quando o usuário efetuar login, você pode recuperar o token usando FirebaseInstanceId.getInstance (). GetToken () e enviá-lo ao servidor Para Registro. (a menos que você queira armazená-lo localmente para excluir um token antigo de seu servidor)
geekoraul
10
FireBase é inteligente e só chamará o método onTokenRefresh () se não houver token (foi excluído ou chamado pela primeira vez) ou se algo mais acontecer e o token tiver sido alterado. Se alguém quiser chamar onTokenRefresh, pode excluir o token e, em seguida, chamar FirebaseInstanceId.getInstance (). GetToken (). A operação FirebaseInstanceId.getInstance (). DeleteInstanceId () precisa estar em AsyncTask ou novo Thread, não pode estar no MainThread !!!
Stoycho Andreev
3
Por que não apenas chamar FirebaseInstanceId.getToken?
esong
1
bem, funcionou perfeitamente ao chamar IntentService, e não há necessidade de salvar o token em prefs, eu acho. Como o valor não muda até FirebaseInstanceId.getInstance (). DeleteInstanceId (); é chamado. Meio que salvou meu dia. :)
Detoxic-Soul
5
Por que salvar o token nas preferências compartilhadas - se você pode chamar FirebaseInstanceId.getInstance (). GetToken () a qualquer momento para obter seu valor?
Alexander Farber
18

Tente implementar FirebaseInstanceIdServicepara obter o token de atualização.

Acesse o token de registro:

Você pode acessar o valor do token estendendo FirebaseInstanceIdService . Certifique-se de ter adicionado o serviço ao seu manifesto , chame getTokenno contexto de onTokenRefreshe registre o valor conforme mostrado:

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

Código Completo:

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

Veja minha resposta aqui .

EDITAR% S:

Você não deveria iniciar um FirebaseInstanceIdService sozinho.

Ele será chamado quando o sistema determinar que os tokens precisam ser atualizados. O aplicativo deve chamar getToken () e enviar os tokens a todos os servidores de aplicativos.

Isso não será chamado com muita frequência, é necessário para a rotação da chave e para lidar com as alterações de ID da instância devido a:

  • O aplicativo exclui o ID da instância
  • O aplicativo é restaurado em um novo usuário do dispositivo
  • desinstala / reinstala o aplicativo
  • O usuário limpa os dados do aplicativo

O sistema acelerará o evento de atualização em todos os dispositivos para evitar sobrecarregar os servidores de aplicativos com atualizações de token.

Experimente a maneira abaixo :

você chamaria FirebaseInstanceID.getToken () em qualquer lugar fora de seu thread principal (seja um serviço, AsyncTask, etc), armazenaria o token retornado localmente e enviaria para seu servidor. Então, sempre que onTokenRefresh()for chamado, você chamará FirebaseInstanceID.getToken () novamente, obterá um novo token e enviará para o servidor (provavelmente incluindo o token antigo também para que seu servidor possa removê-lo, substituindo-o pelo novo) .

pRaNaY
fonte
2
Eu implementei FirebaseInstanceIdService, o problema é que onTokenRefresh () está sendo chamado quase imediatamente após o usuário instalar o aplicativo. eu preciso que ele seja chamado depois de fazer um Login / Inscrição
TareK Khoury
1
Portanto, a exclusão do FirebaseInstanceId atualizará o token, obrigado!
Louis CAD
após GCM para FCM, FirebaseInstanceId.getInstance (). getToken (); sempre retorna nulo. Qualquer solução?
Govinda Paliwal de
@TareKhoury Você pode chamar esse método sempre que necessário para obter o token. FirebaseInstanceId.getInstance (). GetToken ();
sssvrock
@pRaNaY no caso de a atualização do aplicativo cliente onTokenRefresh()ser chamada?
Piyush Kukadiya
2

Estou mantendo um sinalizador na preferência compartilhada que indica se o token gcm foi enviado ao servidor ou não. Na tela inicial, sempre que chamo um método sendDevicetokenToServer. Este método verifica se o ID do usuário não está vazio e o status de envio do gcm, em seguida, envia o token para o servidor.

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

}

}

Na classe 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");
}
Prashanth Debbadwar
fonte
2

Galera tem solução bem simples

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

Nota: Se seu aplicativo usou tokens que foram excluídos por deleteInstanceID, seu aplicativo precisará gerar tokens de substituição.

Em vez de excluir o Id da instância, exclua apenas o token:

String authorizedEntity = PROJECT_ID;
String scope = "GCM";
InstanceID.getInstance(context).deleteToken(authorizedEntity,scope);
Manav Patadia
fonte
2
Não funcionou para mim. Depois de chamar deleteToken (), getToken () retorna o mesmo token de antes e onTokenRefresh não foi chamado.
Lera
1

Isso está no RxJava2 no cenário quando um usuário faz logout de seu aplicativo e outros usuários fazem login (mesmo aplicativo) Para registrar e fazer login API de login)

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

Conecte-se

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

);
Aklesh Singh
fonte
0

Esta resposta não destrói o id da instância, em vez disso, é capaz de obter o atual. Ele também armazena um atualizado nas preferências compartilhadas.

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 (qualquer classe onde você deseja definir / obter preferências)

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

}

O Android 2.0 e superior onCreatedo serviço não é invocado quando iniciado automaticamente ( fonte ). Em vez disso, onStartCommandé substituído e usado. Mas no FirebaseInstanceIdService real, ele é declarado como final e não pode ser substituído. No entanto, quando iniciamos o serviço usando startService (), se o serviço já estiver em execução, sua instância original é usada (o que é bom). Nosso onCreate () (definido acima) também foi chamado !.

Use isso no início de MainActivity ou em qualquer ponto que você ache que precisa do id da instância.

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

E finalmente,

Utility.getFirebaseInstanceId(getApplicationContext())

Observe que você pode aprimorar isso ainda mais tentando mover o código startservice () para o método getFirebaseInstanceId.

Varun Garg
fonte
Se você redefinir o aplicativo / executar pela primeira vez, levará algum tempo para atualizar o token. Portanto, você obterá a string "padrão" por um ou dois minutos.
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();
        }
    }
}
Sayed Azharuddin
fonte
0

FirebaseInstanceIdService

Esta classe está obsoleta. A favor da substituição de onNewToken em FirebaseMessagingService. Uma vez que tenha sido implementado, este serviço pode ser removido com segurança.

A nova maneira de fazer isso seria substituir o onNewTokenmétodo deFirebaseMessagingService

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

Também não se esqueça de adicionar o serviço no Manifest.xml

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

Como eu atualizo meu deviceToken

Primeiro, quando faço o login, envio o primeiro token do dispositivo na coleção do usuário e o usuário conectado no momento.

Depois disso, substituo onNewToken(token:String)meu FirebaseMessagingService()e atualizo esse valor se um novo token for gerado para esse usuário

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

Cada vez que seu aplicativo é aberto, ele verifica se há um novo token; se o usuário ainda não estiver conectado, ele não atualizará o token; se o usuário já estiver conectado, você pode verificar se há um newToken

Gastón Saillén
fonte