ichthyocentaurs
ichthyocentaurs

Reputation: 2175

FCM Token Issue

I had UrbanAirship implemented in version 1 of the app. Now I extended FirebaseMessagingService in version 2 of the app. I am not getting a call in onNewToken() to be able to send the token to my servers. My boilerplate code looks like

AndroidManifest.xml

  <service
        android:name=".services.fcm.PushMessageReceiver"
        android:enabled="true"
        android:exported="true"
        android:stopWithTask="false">
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
        </intent-filter>
    </service>

and Receiver

        public class PushMessageReceiver extends FirebaseMessagingService {  ...
            @Override
            public void onMessageReceived(RemoteMessage remoteMessage) {
            ...
            }

            @Override
            public void onNewToken(String s) {
            Log.i(Config.LOGTAG, "######**** new token for fcm called");
                Context ctx =ApplicationCustom.getContext();
                SharedPreferences preferences = ctx.getSharedPreferences(Config.SHARED_PREFERENCES, Context.MODE_PRIVATE);
                preferences.edit().putString(Config.SHARED_PREFS_DEVICE_TOKEN, s).apply();
                Intent intent = new Intent(this, XmppConnectionService.class);
                intent.setAction(XmppConnectionService.ACTION_FCM_TOKEN_REFRESH);
                intent.putExtra("token", s);
                startService(intent);
                pushToServer();
            }

               public static void getToken() {
                Log.i(Config.LOGTAG, "######**** get token for fcm called");

                try {
                    Log.i(Config.LOGTAG, "######**** delete token for fcm called");
                    FirebaseInstanceId.getInstance().deleteInstanceId();
                    FirebaseInstanceId.getInstance().getInstanceId();
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.w(Config.LOGTAG, "######**** delete InstanceId failed", e);
                }

                FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(task
        -> {
                    if (!task.isSuccessful()) {
                        Log.w(Config.LOGTAG, "getInstanceId failed", task.getException());
                        return;
                    }

                    Log.i(Config.LOGTAG, "######**** getInstanceId successful");

                    // Get new Instance ID token
                    String token = task.getResult().getToken();
                    Context ctx = ApplicationCustom.getContext();
                    SharedPreferences preferences = ctx.getSharedPreferences(Config.SHARED_PREFERENCES, Context.MODE_PRIVATE);
                    preferences.edit().putString(Config.SHARED_PREFS_DEVICE_TOKEN, token).apply(); 
            pushToServer();
           }); 
        }

    public void pushToServer(){
        // Logic to push token to a server reading from preferences
    }
}

Observations:

1) onNewToken never gets called for apps that are being updated.

2) new installs get a token

3) after I added a call to FirebaseInstanceId.getInstance().deleteInstanceId() OnComplete does not get called either.

4) A call to getToken(senderId, "FCM") on real phones (not emulators) invariably results in

java.io.IOException: TOO_MANY_REGISTRATIONS
    at com.google.firebase.iid.zzr.zza(Unknown Source:66)
    at com.google.firebase.iid.zzr.zza(Unknown Source:79)
    at com.google.firebase.iid.zzu.then(Unknown Source:4)
    at com.google.android.gms.tasks.zzd.run(Unknown Source:5)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.lang.Thread.run(Thread.java:764)

how do I fix observation 1. Is it because the token has already been delivered to UrbanAirship that onNewToken does not get called? Fyi getToken is called in a service onCreate() method.

implementation 'com.google.firebase:firebase-messaging:17.3.4'

Upvotes: 2

Views: 5384

Answers (3)

Dulanga
Dulanga

Reputation: 931

Some time onTokenRefresh() method call with some delay and it will generate token when new install happen that how its behave their for we need to implement functionality like below to overcome those issue maintain new user login also

public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {


        private String TAG = getClass().getName();

        public static final String TOKEN_BROADCAST = "myfcmtokenbroadcast";

        @Override
        public void onTokenRefresh() {

            //For registration of token
            String refreshedToken = FirebaseInstanceId.getInstance().getToken();

            //To displaying token on logcat
            Log.d("TOKEN: ", refreshedToken);

            //calling the method store token and passing token
            getApplicationContext().sendBroadcast(new Intent(TOKEN_BROADCAST));
            storeToken(refreshedToken);

        }

        private void storeToken(String token) {
            //we will save the token in sharedpreferences later
            SharedPrefManager.getInstance(getApplicationContext()).saveDeviceToken(token);
        }


    }

In your onCreate method in MainActivity class call this methord

private void registerFCMToken(){
        registerReceiver(broadcastReceiver, new IntentFilter(MyFirebaseInstanceIDService.TOKEN_BROADCAST));
        final boolean isRegisterFcm = preferences.getBoolean("IS_REGISTER_FCM", false);
//      FCM token Register when onTokenRefresh method call
        broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String fcmToken = SharedPrefManager.getInstance(MainActivity.this).getDeviceToken();
                if(!isRegisterFcm) {
                    RegisterFcmTokenRequest request = new RegisterFcmTokenRequest();
                    request.setFcmtoken(fcmToken);
                    performRegisterFcmRequest(request);
                }
            }
        };

//        FCM token Register when new user Login
        if(SharedPrefManager.getInstance(this).getDeviceToken() != null && !isRegisterFcm) {
            String fcmToken = SharedPrefManager.getInstance(MainActivity.this).getDeviceToken();
            RegisterFcmTokenRequest request = new RegisterFcmTokenRequest();
            request.setFcmtoken(fcmToken);

            performRegisterFcmRequest(request);
        }
    }

In the onDestroy method

 unregisterReceiver(broadcastReceiver);

This class maintains the Shredpreferance for FCM token

public class SharedPrefManager {
private static final String SHARED_PREF_NAME = "FCMSharedPref";
private static final String TAG_TOKEN = "tagtoken";

private static SharedPrefManager mInstance;
private static Context mCtx;

private SharedPrefManager(Context context) {
    mCtx = context;
}

public static synchronized SharedPrefManager getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new SharedPrefManager(context);
    }
    return mInstance;
}

//this method will save the device token to shared preferences
public boolean saveDeviceToken(String token){
    SharedPreferences sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPreferences.edit();
    editor.putString(TAG_TOKEN, token);
    editor.apply();
    return true;
}

//this method will fetch the device token from shared preferences
public String getDeviceToken(){
    SharedPreferences sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
    return  sharedPreferences.getString(TAG_TOKEN, null);
}

}

Upvotes: 1

Abdullah Riaz
Abdullah Riaz

Reputation: 556

That's okay if your onNewToken() is not called. You can get the latest token already made by firebase for your device. onNewToken() is called on specific occasions.

The registration token may change when:

-The app deletes Instance ID
-The app is restored on a new device
-The user uninstalls/reinstall the app
-The user clears app data.

Do read the firebase documentation : https://firebase.google.com/docs/cloud-messaging/android/client#retrieve-the-current-registration-token

And for your second query, deleteInstanceId is a blocking call, so you will have to do it in a background thread. like this,

new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        FirebaseInstanceId.getInstance().deleteInstanceId();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();

Upvotes: 1

Sandeep Malik
Sandeep Malik

Reputation: 1976

you can get fcm token by this:-

FirebaseInstanceId.getInstance().getInstanceId()
                .addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
                    @Override
                    public void onComplete(@NonNull Task<InstanceIdResult> task) {
                        if (task.isSuccessful()) {
                            String firebaseToken = task.getResult().getToken();
                        } else {
                            getFirebaseToken();
                        }
                    }
                });

Upvotes: 3

Related Questions