33

I am using FCM to provide notifications in my app. Everything worked well, but now I realised that, when I install my application using Android Studio (not from GooglePlay) the token is null at first run. When I close my app and restart it, the token is not null anymore. What cause this problem and how can I avoid it?

InstanceIDService:

public class InstanceIDService extends FirebaseInstanceIdService {

    @Override
    public void onTokenRefresh() {

        String token = FirebaseInstanceId.getInstance().getToken();
        registerToken(token);
    }

    private void registerToken(String token) {

        OkHttpClient client = new OkHttpClient();
        RequestBody body = new FormBody.Builder()
                .add("Token",token)
                .build();

        Request request = new Request.Builder()
                .url("url_to_registration_script")
                .post(body)
                .build();

        try {
            client.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

In MainActivity:

FirebaseMessaging.getInstance().subscribeToTopic("topic");
String token = FirebaseInstanceId.getInstance().getToken();
Log.d("TOKEN", token);

Last log returns null when app is installed and started for the first time

Registration script:

 <?php 
 if (isset($_POST["Token"])) {

       $_uv_Token=$_POST["Token"];
       $conn = mysqli_connect("localhost","user","pass","db") or die("Error connecting");
       $q="INSERT INTO users (Token) VALUES ( '$_uv_Token') "
          ." ON DUPLICATE KEY UPDATE Token = '$_uv_Token';";

  mysqli_query($conn,$q) or die(mysqli_error($conn));
  mysqli_close($conn);
}
?>
Blo
  • 11,903
  • 5
  • 45
  • 99
Matej Košút
  • 590
  • 2
  • 10
  • 27

10 Answers10

30

The token is fetched asynchronously on first app start. You have to wait for onTokenRefresh() to be called in your FirebaseInstanceIdService before the token can be accessed.

patloew
  • 1,090
  • 9
  • 13
  • my doubt is why we need to call this every time FirebaseMessaging.getInstance().subscribeToTopic("topic"); before getting token instance. – Ruban Jun 17 '17 at 04:24
  • Actually String refreshedToken = FirebaseInstanceId.getInstance().getToken("send_id", "FCM"); is the right answer. – Gillis Haasnoot Apr 19 '18 at 21:55
9

uninstall the app from emulator and run again , so that the onTokenRefreshed method will be called again .

To check wether you are already registered and you just want to know the FCM TOken

String refreshedToken = FirebaseInstanceId.getInstance().getToken();

add the above line in MyFirebaseMessagingService class any where(in oncreate method ) and just Toast or log the refreshedToken.

5

Just replace:

String token = instanceID.getToken();

with:

String token = instanceID.getToken($SENDER_ID, "FCM");

and it will work.

Dan Lowe
  • 51,713
  • 20
  • 123
  • 112
Alaa AlDarf
  • 69
  • 2
  • 3
  • What is $SENDER_ID here? – Muhammad Noman Feb 02 '18 at 07:24
  • To get FCM Sender ID: Open the Firebase Console. Find your Firebase project and click Project Name. click the setting icon and select "Project Setting" menu Select "Could Messaging" tab and use sender ID on the page – Alaa AlDarf Feb 25 '18 at 12:45
  • Hi Alaa, I am getting below exception by calling this live. java.io.IOException: MAIN_THREAD try { String Token = FirebaseInstanceId.getInstance().getToken("", "FCM"); int a = 0; a++; } catch (IOException e) { e.printStackTrace(); } – Muhammad Noman Feb 26 '18 at 12:48
  • M.Homan run that code on other `Thread`. Why this code is working after sending `Sender Id`? Any explanation? – Varad Mondkar May 01 '18 at 12:58
4

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.

Marline
  • 269
  • 3
  • 10
2

Even I had the same issue. I had given the Log statement to print token in onCreate of my launcher activity. It takes time to refresh the token once you uninstall the app. Thats why you get null. You have to wait for a bit to get the token refreshed.

Rakesh
  • 1,205
  • 1
  • 14
  • 33
2

Sometimes network issues may occur, even internet is working fine.... check your FirebaseInstancId class defined in Manifest file in your android project.

KeyMaker00
  • 6,194
  • 2
  • 50
  • 49
1

I just fell in same issue and this is the way I fixed it. Call the block somewhere in onCreate() method of your activity. I want to sent the Token to my Parse server so you must change it based on your needs.

This is the sample I followed.

private void subscribeUserToParse() {
        String deviceToken = FirebaseInstanceId.getInstance().getToken();
        if (TextUtils.isEmpty(deviceToken)) {
            Intent intent = new Intent(this, MyFirebaseInstanceIDService.class);
            startService(intent);
            return;
        }

        User user = UserUtil.retrieveUserFromDB(mRealm);
        String objectId = user.getParseObjectId();
        if (!TextUtils.isEmpty(objectId)) {
            ParseUtils.subscribeWithUsersObjectId(objectId, deviceToken);
        }
    }
Hesam
  • 52,260
  • 74
  • 224
  • 365
1

The biggest issue i faced after writing the correct code is:- not the latest version of google play services in build-gradle, So always use the google-play-services version shown by firebase in setting up dialog

Chirag Joshi
  • 409
  • 1
  • 4
  • 13
  • Is this really a solution to the presented problem? – yakobom Sep 26 '17 at 04:19
  • yes i was trying since last night that why my code not showing token of firebase then i upgrade my version number on google-play-services in build gradle now its working – Chirag Joshi Sep 26 '17 at 04:47
1

I was having the same problem in a Android 4.2 device. So, to solve the problem, first, check if your service, in the manifest, if it have a priority like this:

    <service
        android:name=".other.MyFirebaseInstanceIDService"
        android:exported="false">
        <intent-filter android:priority="1000">
            <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
        </intent-filter>
    </service>

Second, use a broadcast intent to notify your MainActivity that changed the token:

public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {

@Override
public void onTokenRefresh() {
    // Get updated InstanceID token.
    String refreshedToken = FirebaseInstanceId.getInstance().getToken();

    final Intent intent = new Intent("tokenReceiver");
    final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(this);
    intent.putExtra("token",refreshedToken);
    broadcastManager.sendBroadcast(intent);
}

Then in MainActivity, in the onCreate, add this:

     @Override
protected void onCreate(Bundle savedInstanceState) {
...
LocalBroadcastManager.getInstance(this).registerReceiver(tokenReceiver,
            new IntentFilter("tokenReceiver"));
}

and, in the MainActivity, add a new BroadcastReceiver:

BroadcastReceiver tokenReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String token = intent.getStringExtra("token");

        if(token != null)
        {
            Log.e("firebase", String.valueOf(token));

            // send token to your server 
        }

    }
};
1

Since the getToken() method is deprecated, you have to replace it with a listener.

Just replace String token = FirebaseInstanceId.getInstance().getToken(); by the following code snippet:

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();
                            }
});

You can find the official google documentation here: https://firebase.google.com/docs/cloud-messaging/android/client

And a nice medium post will help you out as well: https://medium.com/@cdmunoz/fcm-getinstance-gettoken-in-android-is-now-deprecated-how-to-fix-it-3922a94f4fa4

user2912903
  • 182
  • 2
  • 4