14

Firebase has deprecated some of the messaging calls in the com.google.firebase:firebase-messaging:17.1.0 release. This post goes over those changes nicely.

Question: Can someone tell me if is it considered bad practice to not implement onNewToken and instead just call the below block each app launch This probably seems excessive to Android folks but feels like home from an iOS point of view.

FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener(this) { instanceIdResult ->
    // Just use this call 
    val newToken = instanceIdResult.token
    Log.i("newToken", newToken)
}



@Override
public void onNewToken(String s) {
    super.onNewToken(s);
    // Leave this unimplemented
}

I am more familiar with iOS which calls its onNewToken equivalent on every app launch. So for iOS I put logic there to determine if my backend needs to be updated.

getInstanceId() docs say This generates an Instance ID if it does not exist yet, which starts periodically sending information to the Firebase backend. This makes me assume I can just call FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener each launch.

kev
  • 2,216
  • 2
  • 22
  • 31

6 Answers6

8

First of all, I'm highly skeptical of any logic that suggests that if something is OK in iOS, that it would be OK on Android!

The implementation of push messaging between Android and iOS is extremely different. On Android, it's dependent on Play Services, which runs in another process. On iOS, it's something completely different. The rules of engagement are simply not at all the same.

Note that the suggested token retrieval method is via callback. That is suggesting that token generation is essentially asynchronous. In other words, at app launch (in whatever way you actually define that), the background stuff that manages to token might not be done with that yet. There simply might not be any token available when you ask for it. And who knows how long that takes? You're better off just accepting the token when the system tells you it's ready instead of making a guess about when it's ready. Follow the recommended implementation path.

Doug Stevenson
  • 236,239
  • 27
  • 275
  • 302
  • "Follow the recommended implementation path." what is the recommended implementation path to send firebase token to my server? – kerollos Apr 09 '20 at 07:49
8

Something very important that no one has mentioned yet:

If you check for the current device token only after an app launch, you might loose the event of the token being updated while your app is in the background (of course) and you won't be able to receive remote push messages from your server until the user launches the app again and you send the new token to the server.

The whole purpose of having that callback which can also be called while your app is in the background is to prevent loosing backend messages (important if your app or some important features of it relies a lot on push notifications). It is important to be aware that this callback will not only deliver you the token when you register the device for the first time but also: Called if InstanceID token is updated. This may occur if the security of the previous token had been compromised.

So:

Can someone tell me if is it considered bad practice to not implement onNewToken and instead just call the below block each app launch This probably seems excessive to Android folks but feels like home from an iOS point of view.

Yes, it is actually a bad practice to not implement onNewToken().

Hugo Allexis Cardona
  • 1,097
  • 15
  • 23
  • Not really, because if firebase needs to call onNewToken on your service, it needs to start your process, hence running App.onCreate first, so if your sending is there youre fine. But im not sure about the wakelocks – urSus Oct 26 '19 at 03:14
7

Despite the documentation says that onNewToken is called upon the first app start, it is not. That is why I use FirebaseInstanceId respectively getToken() when I need the Id while onNewToken has not been called before although the app is already running for a while. (So we do both in our project)

What I observe is that Firebase will call onNewToken shortly after I was fetching the token via FirebaseInstanceId. It seems that fetching the token this way initiates something within the Firebase service.

However, it works that way and that is good enough for our current project.

Edit: As getToken() of FirebaseInstanceId got deprecated recently, please refer to Arthur Thompson's answer.

Hermann Klecker
  • 13,792
  • 4
  • 45
  • 69
  • 1
    agreed, onNewToken is definitely not called on it's own like the document says. My login uses the token for authentication and every new install basically always gets an error due to this approach. – Kushan Aug 03 '18 at 13:04
  • I'm curious as to whether the Quick Start project: https://github.com/firebase/quickstart-android/tree/master/messaging/app/src/main even works, i.e. receives a token at all (I have not tried it as yet because I'm using a different development tool) without clicking the logToken button, which uses FirebaseInstanceId.getInstance().getInstanceId(). If I attempt to call that, my app crashes – Dave Nottage Aug 05 '18 at 04:32
  • EDIT: whoops I was talking about `onTokenRefresh()` not `onNewToken()`.I have done some testing on this and `onTokenRefresh()` is called after app first launch, but it takes a few seconds before it is called. `getToken()` will return `null` before you receive a token from Google. Tested on 17.0.0 – Smalls Mar 05 '19 at 22:41
  • @Override method "onNewToken" is not found FirebaseMessagingService.Try to find the method in FirebaseMessagingService but no success.Anyone having any idea? – blackjack Mar 14 '19 at 05:57
  • Please, do not refer to Arthur Thompson's answer as correct, as he's wrong in that `FirebaseInstanceId.getInstance().getInstanceId()` is unnecessary. I don't receive token via `onNewToken()` unless I use `getInstanceId()`. See https://stackoverflow.com/a/51835126/231590 – soshial Apr 22 '20 at 09:45
4

Calling FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener on every app launch is an option (an unnecessary option), onNewToken is there specifically to provide you access to the token when available.

Note that calling FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener on every app launch would require you to handle the case when the token is not yet available, using onNewToken avoids that.

Arthur Thompson
  • 8,335
  • 3
  • 26
  • 31
  • 1
    *onNewToken is there specifically to provide you access to the token when available.* Have a look here about **`onNewToken()`** https://stackoverflow.com/a/51835126/7666442 – AskNilesh Aug 29 '18 at 09:47
  • I using `FCM` but on KITKAT device onTokenRefresh method not triggered – Sagar Nov 21 '18 at 06:15
1

Let's simplify this equation - you should do both.

In our app we were not manually checking FirebaseMessaging.getInstance().getToken(), but did have the onNewToken override set up. This worked for most users, but we had instances of people who would stop receiving notifications unless they reinstalled the app.

So if you really want to be sure you always have an up to date token, you need to do both here. Check at startup with getToken() to make sure you didn't miss an update, and subscribe to onNewToken to get notified if it changes in between app launches. Firebase documentation does allude to this, although to be honest it could be more clear:

Screenshot of Firebase documentation

Deeko
  • 1,464
  • 9
  • 26
0

The onNewToken() function works like it's predecessor, onTokenRefresh().

Implementing the first block on your post requests and waits for a token. This guarantees that a token (or an exception) would return. However, that token isn't guaranteed to stay the same forever. Similar with onTokenRefresh(), onNewToken() is triggered when a token for the corresponding app instance is generated, which in turn you should use and replace with the old token you got.

My answer here has more details.

AL.
  • 33,241
  • 9
  • 119
  • 257
  • Is it really the same? Because after migrating I get a RuntimeException when I try to start an AsyncTask which worked before... – user754730 Aug 08 '18 at 13:09
  • @user754730 I updated our app myself and it worked the same way. Maybe there's a different factor as to why you're encountering the exception. I suggest posting a question so the community could help. Make sure to provide as much as (necessary) details as you can. Cheers! – AL. Aug 08 '18 at 15:46
  • @ AL, Regarding your answer "The onNewToken() function works like it's predecessor, onTokenRefresh()." As per my understanding,In case of multiple tokens(by getting from multiple sender id) onTokenRefresh is called whenever any of token need to be refreshed (**Not only default token which is received from default sender id**) Is this behaviour same with onNewToken? Why this confusion is because the documentation says onNewToken is "**Called when a new token for the default Firebase project is generated.**" In my case, I want to get callback whenever any of the token need to be refreshed. – Hey You Sep 24 '18 at 18:16
  • @HeyYou. If you `onNewToken()` is triggered, the best approach would be is to call `getToken()` for all your other senders and update as necessary -- just like what was advised to do from when it was `onTokenRefresh()`. I'm unable to grasp which part is confusing. – AL. Sep 25 '18 at 04:30
  • @ AL, actualy I have same understanding of what approach you said above. The confusing part is firebase documentation says, onNewToken works for default firebase project only. And it will not be called when other token need to be refreshed. Also, they have not mentioned in document that getToken would be called for all other sender id. – Hey You Sep 25 '18 at 04:38
  • I see. I get what you mean now. AFAIK, currently, there is no way to know if it a token from a different sender is expired. However, the conditions for a token expiring is the same for all, if one of your tokens in the same device expires (right now this can only be for the default for sure), presumably, all others associated is expired as well. Hence having the need to call `getToken()` for all. – AL. Sep 25 '18 at 04:47
  • I using FCM but on `KITKAT` device `onTokenRefresh` method not triggered – Sagar Nov 21 '18 at 06:13