Our business logic requires us to instantiate Firebase during runtime. We fetch the Firebase credentials (App ID, API key etc.) from a default Firebase location after knowing the user's public key and create a Firebase instance using those credentials.
This means that there are two Firebase instances used within the app:
The default "index" Firebase that gives us credentials for the
second
The actual Firebase that we intend to use from that point
forward
The second Firebase is initialised like this:
FirebaseApp app = FirebaseApp.initializeApp(<context>, <options>, <app_label>);
Our problem is that the traditional method of retrieving the FCM token using the FirebaseInstanceIdService and onTokenRefresh() fails since the onTokenRefresh() method is called only by the first Firebase instance.
Directly calling String token = FirebaseInstanceId.getInstance(app).getToken(); returns null as it is never ready when called. We have even tried polling this to test if a token is generated at some point. No luck.
How can we generate an FCM token reliably from a Firebase instance instantiated during runtime?
In general, only the traditional method of getting the token (getToken()) is the only one that refers to the main Firebase Instance. This can be called almost anywhere in your app (but is often called in the initial activity). This will return the token associated to the Sender ID that is seen in your google-services.json. It is also possible for this to return null if the token is still being generated. In cases like that, onTokenRefresh() is triggered upon generation.
However, if you intend to generate a FCM registration token for a different Firebase Project, you'll have to make use FirebaseIntsanceId.getInstance.getToken(String authorizedEntity, String scope), where authorizedEntity is the Sender ID of that different Firebase Project and scope is "FCM" (you can use FirebaseMessaging.INSTANCE_ID_SCOPE as default).
In short, you don't need to specify the Firebase Instance when generating a token.
Also see my answers here and here.
Related
My app sends notifications using Firebase Cloud Messaging FCM. For every user, I'm storing the device token in database and I fetch it when I want to notify him. I'm using FirebaseMessagingService with the overridden method onNewToken that updates my database with new tokens. I suppose that this method is called every 1 hour to check token's update, but I was expecting it to be also called when the service is initialized for the first time (after installing and running the app on device). However this is not the case. To remedy this, I could call onNewToken each time the user log in But I would like to know if this is an acceptable way or there is a better one.
To avoid abuse, I leave here extra information on my case :
I run my app on Android Studio emulator and I check the stored token in database, let's call it TOKEN-1.
Now I install the app on my phone and I show the token with String token = FirebaseInstanceId.getInstance().getToken(); Toast.makeText(MainActivity.this, token, Toast.LENGTH_LONG).show();
The token is different that the first one TOKEN-1, and TOKEN-1 is still stored in my database. This means that I can receive notifications only on emulator and not my phone.
Sorry for my long text and looking forward to reading your suggestions.
The FCM SDK and server work together to manage the token in the background, and listening to onNewToken ensures that you get notified when the token changes. For this reason you should always be listening to onNewToken, and update it in your own database whenever it changes.
There is no guarantee that your FCM token will be refreshed every hour (or even ever) though, as you seem to expect. Given the 1 hour interval, you might be thinking of Firebase Authentication ID tokens, which are short-lived and are indeed refreshed every hour.
Finally: the token doesn't get refreshed when you attach a listener. In fact: if the token was already generated before you attach a listener, your listener won't be called. For this reason, you'll typically also want to grab the current token in your main activity when the app starts, and store it in the database at that point.
This last code is mostly necessary during development, as that's where you're most likely to have the scenario where the token gets generated when you don't have an onNewToken listener yet. So instead of putting code in the main activity, you can also uninstall/reinstall the app after adding your onNewToken listener, as FCM will generate a new token upon installing the app in that case - and thus call your onNewToken with this initial token.
I'm using the following line of code to retrieve the FirebaseInstanceId:
FirebaseInstanceId.getInstance().id
I'm using it as a unique app id and want to know when it changes, because I save it on the server side + I want store it in my shared preferences, so I need to know when it is refreshed.
Do I do this in FirebaseMessagingService's onNewToken(token: String), even though this method is meant for the FCM token? To my understanding, this the instance id is tightly coupled with the FCM token, and is in fact the prefix of it. Can there be a situation where the instance id is refreshed, but the FCM token remains the same (or vice versa)?
If so, what do I do in case my app doesn't need FCM, but I still want to store it? Implement it anyway?
Documentation on FirebaseInstanceId states the following:
Instance ID is stable except when:
App deletes Instance ID
App is restored on a new device
User uninstalls/reinstall the app
User clears app data
So, the way I see it, you should be reasonably safe because that id can be either removed by your own code (via Firebase#deleteInstanceId()) or by the users when they restore/reinstall/uninstall their apps. There're no other cases where this id could be changed throughout the app's life.
From where and when is the token retrieved and when is it available?
Is it a synchronous call to the Firebase server? If I call it too soon in the app lifecycle, might it not have been populated yet?
From where and when is the token retrieved and when is it available?
The token is generated by the FCM Instance ID service in the background, which starts as soon as your app runs. The details on how the token gets generated is unclear, but how I see it is that the device needs a decent connection to the internet in order for it to communicate with the FCM servers for the token.
Is it a synchronous call to the Firebase server?
Technically speaking, no. As mentioned in the docs:
FirebaseInstanceID.getToken() returns null if the token has not yet been generated.
At this time, if the token is null, you should expect a trigger in your onNewToken() where you could then call getToken() which should now contain the token.
If I call it too soon in the app lifecycle, might it not have been populated yet?
It's usually okay to call getToken() as soon as possible -- in your app's MainActivity -- in most cases, by the time your app reaches that point, it already has a value. But then again, you should still handle it properly if it is null.
I don't want to have login or signup for my app. I've implemented fire-base notification in the app as well.
Can I use Fire-base FCM ID (token) as a unique user identifier. Instead of creating a user in the system I'll create a FCM table(id(pk), FCMID) and store all information of the user against that FCM id.
If yes then at what instance it changes the FCM id.
You can but you shouldn't
FCM token can change if you see the Service used for getting the FCM token, the one that has to extend FirebaseInsanceIdService the method inside is called onTokenRefresh() because it will be triggered every time the FCM token is refreshed for a new one. In previous Google Cloud Messaging (GCM) this strike to me as erratic, but in current FCM it seems to be infrequent.
You could do this by saving the current token in some form of local persistence (sharedpreference, database, etc). Then when the FCM is refreshed using the stored FCM the nodes in the real-time database can be updated.
This will have some bad scenarios: what happens if the user re-login by reinstalling or changing device? The first can be solved by saving something unique in the device, I have seen that some sort of devices ids can be get, but those are reset after boot or factory reset. Then a more general solution would make the user to input something unique to them every time they log in. It could be the email, or it could be the phone number.
Which leads me to, use phone authentication instead
i hope tis is helpful to
this is mydb in fcm
db structure in json like,
walinnstracker{
active_users{
info{
"count" : "13",
"female_count" : "0"
"male_count" : "0"
}
}
}
As I read from documentation and other sources it is advised to call FirebaseInstanceId.getInstance().getToken(); inside onTokenRefresh() to make sure that we get the updated token
what I tried was to call it every time the app is opened and its returning value always, and I am wondering is that a thing to trust ?(is it guaranteed to generate the token every time its called? ) or is there any case it might return null ?
since I don't want users logged in to multiple devices with the same account I am hoping to use it as unique identifier.
As per my knowledge and experience I will try to resolve your doubts:
Is it guaranteed to generate the token every time its called?
No it will not generate token every time you call this method. It will just return current token.
Is there any case it might return null ?
Yes it returns null if your token is not yet generated.
I am hoping to use it as unique identifier
Yes you can use it as a unique identifier for each device not for each account. Every device has its unique token.
This is from the official doc:
Retrieve the current registration token
When you need to retrieve the current token, call
FirebaseInstanceId.getInstance().getToken(). This method returns null
if the token has not yet been generated.
For more information refer this link.