I am reading the documentation on this page under the heading "Retrieve the current registration token." I am confused by what it says.
The initial paragraph is very simple. It says we simply have to call...
FirebaseInstanceId.getInstance().getToken
That sounds very easy until I read the next paragraph that says I need to implement MyFirebaseInstanceIDService.java...
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService
{
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
}
So the first paragraph seems to say can make the call anyplace when I want to but the second paragraph has me wraping the call inside an onTokenRefresh().
So this begs the question WHEN does onTokenRefresh get called? With GCM I was able to choose WHEN I made the call asking for the token. With FCM it appears that we wait for onTokenRefresh to be called somewhere in time.
From the same linked document:
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.
It is strongly recommended that onTokenRefresh() is handled since there is a (rare) possibility that calling getToken() would (from the same docs):
This method returns null if the token has not yet been generated.
I think my answer here has a bit more explanation.
With all that said, calling getToken() would (most of the time) give you a token immediately, but in a case that it doesn't, onTokenRefresh() is there.
from this post
The onTokenRefresh() method is going to be called whenever a new token
is generated. Upon app install, it will be generated immediately (as
you have found to be the case). It will also be called when the token
has changed.
Just like an onEdit() function is called when an edit is made or onOpen() is called when the app is opened.
Related
I have two types of questions around FirebaseInstanceId.getToken(String authorizedEntity, String scope), one around calling this method multiple times and one around whether calling this method triggers FirebaseMessagingService.onNewToken(String token).
1) Calling multiple times:
According to this documentation one would call getToken(String authorizedEntity, String scope) multiple times, each time with a different sender id, in order to be able to receive messages from multiple senders. My question is, will each call return a different token, or will each call return the same token but now the token will work also for multiple senders? If we call this method with a sender id that we've previously used before, will that return the existing token or generate a new one?
So, say I have this order of operation
Call getToken("senderId1", "FCM") and get token A
Call getToken("senderId2", "FCM"). Will I get A or a different token B?
Call getToken("senderId2", "FCM"). Will I get A, B, or yet another different one C?
2) Will onNewToken be called?
This documentation states that the method will be invoked if the token changes. So does this mean that if getToken returns a different token than before then onNewToken will be invoked as well? If we're going to be calling getToken multiple times to allow for receiving from different senders, and each call returns a different token, then onNewToken will keep getting invoked.
Since it is advised that we update our server when onNewToken is triggered, I want to understand the expected behavior and avoid generally updating the server on each invocation of onNewToken.
My question is, will each call return a different token, or will each call return the same token but now the token will work also for multiple senders?
getToken() / getToken(String, String) will return the same token until such time that the corresponding token expires. Note that by same token, I mean the same token that they return for each sender. i.e.:
getToken() returns the registration token for the default project (e.g. tokenDefaultSenderId)
getToken(String, String) returns the registration token for the sender it is associated to (e.g. tokenSenderId2)
If we call this method with a sender id that we've previously used before, will that return the existing token or generate a new one?
Okay.
You will get token B.
You will get token B again.
A token is tied to the sender it is associated to.
Will onNewToken be called? ... So does this mean that if getToken returns a different token than before then onNewToken will be invoked as well?
onNewToken() will only return the token for the default sender (emphasis mine):
Called when a new token for the default Firebase project is generated.
The thing about onNewToken() is that it triggers only when the previous token has expired -- the thing to ask is, if the token for the default sender expires, what more for the other senders? So the best workaround here is to call getToken() for each of the sender that you have, like so:
public void onNewToken(String token){
String default = token;
String sender2 = getToken("senderId2", "FCM");
// and so on for each of your sender, then save the tokens as needed
}
The token is unique for every sender id. Different sender ids have different tokens.
Default sender is the one defined in your google-services.json (related to the firebase project that the app is connected to)
OnNewToken is called when token for the default sender is changed. There are no callbacks triggered when a token for a specific sender (other than default) is changed.
As mentioned by #sNash who contacted firebase support:
I contacted firebase support team and got an answer. Answer summary : Different sender id's token will not be automatically managed by firebase cloud messaging. So developer is responsible for it's management. You guys have to check it's validity manually with your own method.
How to determine if token needs to be refreshed in case of multiple sender id?
Managing tokens for specific sender ids (other than default):
One simple solution for managing tokens for specific sender ids (other than default) is through storing all sender ids with their tokens in SharedPreferences or in db. When app starts, check if the token changed for each sender by comparing the stored token with the token returned by
FirebaseInstanceId.getInstance().getToken(SENDER_ID, "FCM");
Moreover, do the same check in onNewToken method. There is a chance that tokens other than the default may be changed when the default token is changed.
My application is receiving the push notification from 2 firebase project. I am getting the tokens for each sender id by calling "getToken(String authorizedEntity, String scope)" separately.
String token1 = FirebaseInstanceId.getInstance().getToken("authorizedEntity1", "FCM");
String token2 = FirebaseInstanceId.getInstance().getToken("authorizedEntity2", "FCM");
As per the onTokenRefresh documentation
Called when the system determines that the tokens need to be refreshed. The application should call getToken() and send the tokens to all application servers.This will not be called very frequently, it is needed for key rotation and to handle Instance ID changes due to:
App deletes Instance ID
App is restored on a new device
User uninstalls/reinstall the app
User clears app data
As onTokenRefresh has been deprecated, I have checked the onNewToken, As per the documentation
Called when a new token for the default Firebase project is generated.
This is invoked after app install when a token is first generated, and again if the token changes.
Q1. How to know which is the default Firebase project in case of multiple sender id ?
Q2. Suppose if "authorizedEntity1" is associated with the default firebase project then does it mean onNewToken will be invoked only when token1 will be changed ? or it will be also invoked when token2 will be changed? If it doesn't work for token2 then how to know that token2 need to be refreshed?
Q3. With reference of this my understanding is onTokenRefresh will be invoked whenever any of the token needs to be refreshed(not only for default project). Is this understanding correct ?
I want to send the updated token to the server whenever system determines that the token1 or token2 need to be refreshed.
Note: I am initializing the firebase in my application class as I am dealing with multiple sender ids.
After some test, I found out that only default project's token will be delivered to onNewToken. onNewToken will not be called when new token created for other sender ids by calling getToken.
Tokens retrieved by calling getToken API are consist of different string data than default token.
And these other sender id's tokens are not refreshed when default token changes.
It look like they last until you explicitly call deleteToken API.
(Token value didn't changed when I repeatedly call getToken.)
Depending on #sNash comment who contaced Firebase Support, you should manage tokens for all sender ids other than the default sender id.
How?
One simple solution is through storing all sender ids with their tokens in SharedPreferences or in db. When app starts, check if the token changed for each sender by comparing the stored token with the token returned by
FirebaseInstanceId.getInstance().getToken(SENDER_ID, "FCM");
Moreover, do the same check in onNewToken method. There is a chance that tokens other than the default may be changed when the default token is changed.
The default sender is the one related to your Firebase project that the app is connected to and it can be found in google-services.json
I've been reading on canonical IDs in GCM and how they help to rectify sending duplicate push notifications and with security. But now with Firebase Cloud Messaging (FCM), does this issue still exist?
I am the registration part has been taken away from the developer now and we just wait for a token refresh as per below:
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
#Override
public void onTokenRefresh() {
// Get updated registration ID
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Logger.d("Refreshed FCM token: " + refreshedToken);
}
}
Some info on canonical IDs can be found here.
Update:
I recently revisited this topic on Canonical IDs and have concluded the following.
In FCM, it seems the Canonical IDs are no longer used (or at the very least extremely rarely) because of the way the Instance ID service works. To put it simply, the service works that there would only be one valid token per App Instance.
If the older token expires (for whichever reason), FCM triggers a tokenRefresh event where you would get a new registration token, and where you must also handle it accordingly (in onTokenRefresh()).
Short answer, Yes. It's still necessary.
The onTokenRefresh() method is expected to trigger whenever the token is actually refreshed. From there, it's the developer's responsibility to send the registration token towards an App Server.
BUT in an event where you weren't able to get a hold of the new registration token (e.g. forgot to save it, deleted it and only have the previous registration token, etc.), it may result to you (the developer) to send towards a supposed to be no longer valid registration token. That's when Canonical IDs come in.
I guess you can treat Canonical IDs as another safety measure so that developers can still get a hold of the valid registration token. Details about Canonical IDs (how it is handled and stuff) are mentioned in the FCM docs here.
I am new to firebase I am learning it like a toddler learning to walk. So far I have managed to send a message to my own phone using a token generated for my phone by firebase framework. Now here's where it gets tricky (in my opinion). There is a method called onTokenRefresh() in the FirebaseInstanceIdService extended service. Since it is called refresh, Then I am assuming that it will change. I want to know when this token is created and when will it be changed?
And if it changes, suppose I send a message to a device with token 'A' which is offline for now, so it will be queued. Now when the device gets online, it will "refresh" the token to 'B'. Now as the message was supposed to be delivered to token 'A', the message will never be delivered. How can I manage this situation?
The token is generated, after the app is first launched, as soon as the phone can connect to the Google servers. Due to the required connectivity this might not happen immediately, but in most of the cases it will happen in few seconds after the user open the app.
As soon as the token is generated the method onTokenRefresh() is called.
As you pointed out the token can change, in which case the onTokenRefresh() method will be called again.
The refresh event is somehow rare, don't expect to see it often at all.
When the refresh token happens, all the messages that have been "successfully" sent (the API returned you a message-id) to the old token will be delivered.
Finally, even after the refresh happened the old token will still be working for a short period, to allow the app to communicate the new token to its back-end.
On initial startup of your app, the sdk of FCM generates the registration token for the client app instance. As above said, It is a rare event. To be specific,The registration token may change when:
The app deletes Instance ID.
The app is restored on a new device
The user uninstall/reinstall the app
The user clears app data.
Instance ID provides a unique ID per instance of your apps.Instance ID provides a simple API to generate security tokens that authorize third parties to access your app's server side managed resources.The Instance ID server can even tell you when the device on which your app is installed was last used.We can use this to decide whether to keep data from the app or send a push message to re-engage with the users.
Every time the device token is changed, It is reflected in onTokenRefresh() method.For getting the device token when it is changed, we can call this method to get the refreshed token.
and to get the device token at any time we can use FirebaseInstanceId.getInstance().getToken() method to get the current device token.It takes a bit of time to get the device token.
Click here to read more about accessing device registration token.
onTokenRefresh() and FirebaseInstanceIdService are deprecated.
This call is also deprecated FirebaseInstanceId.getInstance().getToken()
Instead, You should override onNewToken(String token) in FirebaseMessagingService. This method triggered when the token is changed. Once you override this method, you can safely remove FirebaseInstanceIdService whcih contains onTokenRefresh().
When token can change?
App deletes Instance ID
App is restored on a new device
User uninstalls/reinstall the app
User clears app data
How to retrieve the current token:
by calling FirebaseInstanceId.getInstance().getInstanceId():
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
#Override
public void onComplete(#NonNull Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
Log.w(TAG, "getInstanceId failed", task.getException());
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
// Log and toast
String msg = getString(R.string.msg_token_fmt, token);
Log.d(TAG, msg);
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
});
For more info:
https://firebase.google.com/docs/cloud-messaging/android/client
For Managing tokens for specific sender id (other than the default sender id),
check here
I am using Google's GCM service in my app. I tried the sample code and it worked fine for me. But there is one thing regarding the registration token that confuses me.
The sample code inside the function onHandleIntent(Intent intent) in RegistrationIntentService.java has lines to get the token and then uses is to subscribe the topic
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
I understand that the token refresh may be initiated by the system from reading the sample code MyInstanceIDListenerService.java, and then I will have to notify the server from the callback function onTokenRefresh().
I found that the function onHandleIntent(Intent intent) is always called when I tap the notification to open the app, as a result, the registration token will be generated again. My question is, I can set flat at the point when onTokenRefresh() to determine if I need to update the server and re-subscribe the topic. But can I safely assume the token will never get changed from app launch?
"Does GCM registeration token remain unchanged if app never update and the InstanceID provider never initiate refresh"
The short answer is yes. It never changes. If it does on token refresh will be called. As for the google sample code, there are few vital pieces missing from it which you have to fill up yourself. The first is that RegistrationIntentService does not check if the device is already registered. You should save this information in shared preferences.
If the device is already registered there is no need for this bit of code:
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
Now the question arises what happens if you do call execute this code over and over again? I did experiment with that and found that some devices kept giving the same token over and over again but other devices returned different tokens.