This question already has answers here:
Firebase onTokenRefresh() is not called
(13 answers)
Closed 5 years ago.
I tried to reinstall my application multiple times, I noticed that just about 4 from 10 times that the onTokenRefresh() is called (log output)
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
sendRegistrationToServer(refreshedToken);
}
}
Is there any way to guarantee that onTokenRefresh() to be called so the application will be subscribed to a topic when there is a good internet connection,something like launching a Service that check every time whether the application has subscribed to a topic,if not, relaunching firebase service.
In onCreate() function I did :
mRegistrationBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// checking for type intent filter
if (intent.getAction().equals(Config.REGISTRATION_COMPLETE)) {
// gcm successfully registered
// now subscribe to `global` topic to receive app wide notifications
FirebaseMessaging.getInstance().subscribeToTopic(Config.TOPIC_GLOBAL);
} else if (intent.getAction().equals(Config.PUSH_NOTIFICATION)) {
// new push notification is received
}
}
};
According to docs
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.
So you should send the new token to this server when these events happens!
Related
Now I can send test message from Firebase Console and get a push notification in my phone. I have some queries about generating in-app notification right now. This is my current layout.
I want the push notifications to appear as in-app notifications in my app too. The only class handling the message is MyFirebaseMessagingService class which includes a notificationHelper to help build the notification. How do I pass the message information from MyFirebaseMessagingService to the Notification Fragment I have now? Do I need to store the information in a local file then retrieve the information from the local file to be used in Notification Fragment again? What is the best approach in this case?
public class MyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onMessageReceived(#NonNull RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
if (remoteMessage.getNotification() != null) {
String title = remoteMessage.getNotification().getTitle();
String body = remoteMessage.getNotification().getBody();
NotificationHelper.displayNotification(getApplicationContext(), title, body);
}
}
}
Another trivial question is about the FCM token issue. I have already created a FCM token. How do I make the app to check if a token has been generated to prevent the token be generated every time I launch the app?
if(instanceIdResult.getToken() == null)
{
//generate token
}
Can I write the code like this?
You can use room database. You save all the notifications and then show them in the fragment. If the fragment is already show, you can send and show instantly with broadcastReceiver. Room
FCM token is created once. 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.
You can retrieve the current token like this:
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;
}
String token = task.getResult().getToken();
}
});
you can use sharedpreferences to store number of notifications you get and when yor app opens show the number on notification and if user read them reset the counter . also you can store the token too
I'm using firebase notifications. There are some times when I don't get the pushtoken on time. How can I wait for it?
pushToken = firebaseIDService.getToken();
//SOME CODE
registerUser(pushToken);
So, I just want to stop that function until I get the result of the getToken().
Thanks.
The token is generated asynchronously, and may be refreshed periodically. To ensure your app uses the latest token, implement FirebaseInstanceIdService .onTokenRefresh as shown in the documentation on monitoring token generation:
The onTokenRefreshcallback fires whenever a new token is generated, so calling getToken in its context ensures that you are accessing a current, available registration token. Make sure you have added the service to your manifest, then call getToken in the context of onTokenRefresh, and log the value as shown:
#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.
sendRegistrationToServer(refreshedToken);
}
The sendRegistrationToServer function in the above snippet is something you'd implement yourself, so likely equivalent to registerUser in your code
Warning! Don't use it on main thread!
Also, it's a bad practice (probably), better use answer by #FrankvanPuffelen.
And I'm not sure it's working, but I'm using same approach for few async operations.
#WorkerThread
public String getTokeSync(Context context) {
String token = FirebaseInstanceId.getInstance().getToken();
if (token != null)
return token;
IntentFilter filter = new IntentFilter();
filter.addAction("com.google.android.c2dm.intent.RECEIVE");
filter.addCategory(BuildConfig.APPLICATION_ID);
final CountDownLatch lock = new CountDownLatch(1);
final FirebaseInstanceIdReceiver br = new FirebaseInstanceIdReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
token = FirebaseInstanceId.getInstance().getToken();
lock.countDown();
context.unregisterReceiver(this);
}
}
context.registerReceiver(br, filter);
lock.await(); // Better use await with timeout
return token;
}
I will try to show personal single notification on my phone tray, but I can't rich, so help.
I am having an issue with FireBase Cloud Messaging in which I get the Token from the device and send the notification test through the Google Firebase notification console, however, the notification is never logged nor pushed to the android virtual device. The documentation for FCM is almost exactly the code that I have below and little else in the way of what else you would have to do to get push notifications working with firebase. I have gone through all of the setup information (build.gradle additions, Installing google play services, etc...) as specified in the documentation, but still do not have messages generating. What is wrong with the code that I am not receiving my push notifications to the logcat or the device? Please let me know any further information that would be helpful. Thanks.
mRegistrationBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Config.REGISTRATION_COMPLETE)) {
FirebaseMessaging.getInstance().subscribeToTopic(Config.TOPIC_GLOBAL);
displayFirebaseRegId();
} else if (intent.getAction().equals(Config.PUSH_NOTIFICATION))
{
String message = intent.getStringExtra("message");
Toast.makeText(getApplicationContext(), "Push notification: " + message, Toast.LENGTH_LONG).show();
txtMessage.setText(message);
}
}
};
displayFirebaseRegId();
}
private void displayFirebaseRegId() {
SharedPreferences pref = getApplicationContext().getSharedPreferences(Config.SHARED_PREF, 0);
String regId = pref.getString("regId", null);
Log.e(TAG, "Firebase reg id: " + regId);
if (!TextUtils.isEmpty(regId))
txtRegId.setText("Firebase Reg Id: " + regId);
else
txtRegId.setText("Firebase Reg Id is not received yet!");
}
#Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
new IntentFilter(Config.REGISTRATION_COMPLETE));
LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
new IntentFilter(Config.PUSH_NOTIFICATION));
NotificationUtils.clearNotifications(getApplicationContext());
}
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver);
super.onPause();
}
And I will add lib of fire base messaging is:
compile 'com.google.firebase:firebase-messaging:11.0.4'
You don't need to subscribe inside the BroadcastReceiver you can just do it inside the onTokenRefresh method in the FirebaseInstanceIdService
You don't need to get the push notification in the BroadcastReceiver, you have to do it inside the onMessageReceive in the FirebaseMessagingService
FCM is extremely unreliable with emulators, simply use a real device, I have struggled with this and in some cases I even get the notification days later when opening the emulator for other projects, test this with real phones
I have setup Firebase using Firebase assistant in Android Studio. I am facing one small, but irritating issue. The notifications doesn't work on first launch, but working on all subsequent launches. e.g.
App-installed and opened for first time: Notifications are not received.
If I force-stop the app and restart: Notifications are working now.
I suspect that subscription request is not working on first request.
following is my token refresh code in android FirebaseInstanceIdService:
#Override
public void onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
if(refreshedToken != null){
Log.d(TAG, "Refreshed token: " + refreshedToken);
FirebaseMessaging.getInstance().unsubscribeFromTopic("news");
sendTokenToServer(refreshedToken);
FirebaseMessaging.getInstance().subscribeToTopic("news");
}
}
I do get the registration token in first launch and it is sent to server successfully. But the notification is received only after restarting the app.
Any ideas why it is not working in first launch?
Edited code as suggested in comments. I have saved new token to sharedPrefs and called the Subscription function from activity. BUT THIS IS STILL NOT WORKING. App getting notification only after relaunching the app.
#Override
public void onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
if(refreshedToken != null){
Log.d("FCGM", "Refreshed token: " + refreshedToken);
sendTokenToServer(refreshedToken);
myPrefs settings = new myPrefs(); //my class to handle prefs, working fine
settings.putBool("isNewToken",true);
settings.putString("token",refreshedToken);
}
}
then, in my activity onCreate function, i called the subscription function after a 5 sec delay to make sure that the token is generated and saved.
new Timer().schedule(new TimerTask() {
#Override
public void run() {
myPrefs settings = new myPrefs();
if (settings.getBool("isNewToken")){
Log.d(TAG,"new token found"); //this is reaching
FirebaseMessaging.getInstance().unsubscribeFromTopic("news");
FirebaseMessaging.getInstance().subscribeToTopic("news");
settings.putBool("isNewToken",false);
}
}
}, 5000);
Try moving your topic subscription into your Application subclass or your main activity, the code to subscribe to the topic looks after sending the correct token in the call.
Also by only having the subscription call in onTokenRefresh() what would happen if your user unsubscribed from the topic at a later stage? They wouldn't be able to re-subscribe at a later point.
Turned out the issue was GET request in my sendToken function, which was not finishing off. It should have been a POST method. I have now changed it and is working perfectly. my current onTokenRefresh code is:
#Override
public void onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
if(refreshedToken != null){
FirebaseMessaging.getInstance().unsubscribeFromTopic("news");
sendTokenToServer(refreshedToken);
myPrefs settings = new myPrefs();
settings.putBool("isNewToken",true);
settings.putString("token",refreshedToken);
FirebaseMessaging.getInstance().subscribeToTopic("news");
Log.d(TAG, "Subscribed");
}
}
the send token code looks like this now:
client = (HttpURLConnection) url.openConnection();
client.setRequestMethod("POST");
client.setDoOutput(true);
OutputStream outputPost = new BufferedOutputStream(client.getOutputStream());
outputPost.write(params.getBytes());
outputPost.flush();
outputPost.close();
Hope it helps someone with similar issue.
I have just migrated to FCM. I have added my class that extends from FirebaseInstanceIdService to receive a refreshedToken as and when appropriate.
My question is specific to the case when user installs my app first time and due to some reason, unable to receive a registration Id from onTokenRefresh. How are we supposed to handle this? Can I set a broadcast receiver from my FirebaseInstanceIdService class which will notify the Main activity when a registration Id is received?
if your device have no connection to the internet onTokenRefresh() is never called and you should notify to user his/her device has no internet connection
firebase has its own network change listener and when a device connected to the internet then try to get token and return it, at this time you can tell your main activity by sending a local broadcast receiver that registration token is received.
use below codes:
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d("FCN TOKEN GET", "Refreshed token: " + refreshedToken);
final Intent intent = new Intent("tokenReceiver");
// You can also include some extra data.
final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(this);
intent.putExtra("token",refreshedToken);
broadcastManager.sendBroadcast(intent);
}
in your main activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
LocalBroadcastManager.getInstance(this).registerReceiver(tokenReceiver,
new IntentFilter("tokenReceiver"));
}
BroadcastReceiver tokenReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String token = intent.getStringExtra("token");
if(token != null)
{
//send token to your server or what you want to do
}
}
};
}
Change this in manifest.xml file
tools:node="replace"
to
tools:node="merge".
As far as I know, token will be null only when you try to run your app on emulator on which google play service is not there and when you are using dual email id on you google play store(on you actual device), but only one email id is verified for the usage. Those are the cases which will give you null token and I have already implemented FCM in my new project. So for rest of any cases , token won't be null.
Use this class extends with..
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = "MyFirebaseIIDService";
public static final String REGISTRATION_SUCCESS = "RegistrationSuccess";
#Override
public void onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
Toast.makeText(MyFirebaseInstanceIDService.this,refreshedToken,Toast.LENGTH_LONG).show();
}
}
I was facing the same problem. I looked through a lot of SO posts and other forums and I found a solution that worked for me. FCM documentation says to use this method to get a token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
I found a post online (I apologize, I don't remember which one. I found it a while ago) that used this method instead:
String refreshedToken = FirebaseInstanceId.getInstance().getToken() (String authorizedEntity, String scope);
FCM documentation describes the first method as:
Return the master token for the default Firebase project.
While the second one is described as:
Returns a token that authorizes an Entity to perform an action on behalf of the application identified by Instance ID.
This is similar to an OAuth2 token except, it applies to the application instance instead of a user.
For example, to get a token that can be used to send messages to an application via FirebaseMessaging, set to the sender ID, and set to "FCM".
I have been looking into why the first method call takes a longer time to return a token, but I haven't found an answer yet. Hope this helps.
depending on your application logic you can write the code to handle the "new" token directly in the FirebaseInstanceIdService.onTokenRefresh() method, or you can use a LocalBroadcast to send this information to your activity if you need to change the UI when this event happens.
Note that when onTokenRefresh() is called your activity could be closed.
A possible implementation could a mix of the two options:
add some logic in onTokenRefresh() to send the token to your server
use a LocalBroadcastReceiver to inform your activity, if you have a piece of UI that need to change when the token is available.
If you are running it on your emulator, check that you have Google play services enabled in Tools -> Android -> SDK Manager -> SDK Tools -> Google play services
Once installed, reboot both Android Studio and your emulator
It worked for me