rpattabi rpattabi - 1 month ago 8
Android Question

Do I need to extend FirebaseInstanceIdService to subscribe to FCM Topics?

I want to manage topic subscription from the client (android app). I am currently doing it at activity onCreate(). I am wondering if the right way is to subscribe / unsubscribe at InstanceIdService::onTokenRefresh() or at any time convenient (on button click, etc).

In short, if I manage topic subscription at the client side (no server), do I still have to bother with InstanceIdService?

Different sources of documentation provide different take on Firebase Cloud Messaging (FCM) topic subscription. Some mention InstanceIdService, some not. Here they are:


  1. Firebase Guide on Send Topic Messages with Firebase Console



It does not mention InstanceIdService when talking about topic subscriptions.


Once you've completed the setup tasks, you can add client code to
subscribe to a topic and then handle messages sent to the topic.

Client apps can subscribe to any existing topic, or they can create a
new topic. When a client app subscribes to a new topic name (one that
does not already exist for your Firebase project), a new topic of that
name is created in FCM and any client can subsequently subscribe to
it.

To subscribe to a topic, the client app calls Firebase Cloud Messaging
subscribeToTopic() with the FCM topic name:


FirebaseMessaging.getInstance().subscribeToTopic("news");



  1. Firebase Android Codelab




The class MyFirebaseInstanceIdService will be a service used to handle
FCM logic. This service is used to alert the application when a new
InstanceID token is generated, and to retrieve the generated token.

Modify it to extend FirebaseInstanceIdService and override the
onTokenRefresh method to subscribe to a topic. Use the following code
to update the onTokenRefresh method in MyFirebaseInstanceIdService to
look like this:


public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {

private static final String TAG = "MyFirebaseIIDService";
private static final String FRIENDLY_ENGAGE_TOPIC = "friendly_engage";

/**
* The Application's current Instance ID token is no longer valid
* and thus a new one must be requested.
*/
@Override
public void onTokenRefresh() {
// If you need to handle the generation of a token, initially or
// after a refresh this is where you should do that.
String token = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "FCM Token: " + token);

// Once a token is generated, we subscribe to topic.
FirebaseMessaging.getInstance()
.subscribeToTopic(FRIENDLY_ENGAGE_TOPIC);
}
}



  1. Firebase quickstart project on github



It uses InstanceIdService, but topic subscription does not happen there. It is done simply in client as part of button click in an activity:

Button subscribeButton = (Button) findViewById(R.id.subscribeButton);
subscribeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// [START subscribe_topics]
FirebaseMessaging.getInstance().subscribeToTopic("news");
// [END subscribe_topics]

// Log and toast
String msg = getString(R.string.msg_subscribed);
Log.d(TAG, msg);
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
});


The comment at InstanceIdService code suggests to manager subscription
onTokenRefresh()


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

AL. AL.
Answer

So I've done a quick look around where the FirebaseMessaging.java class content is visible and found this (note pretty sure this is not an official reference to the class, but it's the available one after a quick search):

public void subscribeToTopic(String str) {
    if (str != null && str.startsWith("/topics/")) {
        Log.w("FirebaseMessaging", "Format /topics/topic-name is deprecated. Only 'topic-name' should be used in subscribeToTopic.");
        Object substring = str.substring("/topics/".length());
    }

    if (substring == null || !baY.matcher(substring).matches()) {
        String valueOf = String.valueOf("[a-zA-Z0-9-_.~%]{1,900}");
        throw new IllegalArgumentException(new StringBuilder((String.valueOf(substring).length() + 55) + String.valueOf(valueOf).length()).append("Invalid topic name: ").append(substring).append(" does not match the allowed format ").append(valueOf).toString());
    }

    FirebaseInstanceId instance = FirebaseInstanceId.getInstance();
    String valueOf2 = String.valueOf("S!");
    String valueOf3 = String.valueOf(substring);
    instance.zzsf(valueOf3.length() != 0 ? valueOf2.concat(valueOf3) : new String(valueOf2));
}

In here, you can see that inside the subscribeToTopic() method, should the string parameter get past the checkers (null and format checkers, I think), it gets a FirebaseInstanceId object on it's own:

FirebaseInstanceId instance = FirebaseInstanceId.getInstance();

and then probably initiates the call to subscribe to the topic here:

instance.zzsf(valueOf3.length() != 0 ? valueOf2.concat(valueOf3) : new String(valueOf2));

And as you already know, FirebaseInstanceId is probably a singleton class, where you retrieve your registration token. So I think inside that zzsf method, the name of the topic is passed to another function that generates a request that also includes your registration token towards the server.

Long story short, I don't think it's a requirement for you to extend the FirebaseInstanceIdService to call subscribeToTopic(), however, I think it's essential because (as described in the docs) it is the:

Base class to handle Firebase Instance ID token refresh events.


For your other inquiries.

I want to manage topic subscription from the client (Android app). I am currently doing it at activity onCreate(). I am wondering if the right way is to subscribe / unsubscribe at InstanceIdService::onTokenRefresh() or at any time convenient (on button click, etc)?

I think doing it in onCreate() is okay. If you see my answer here, @FrankvanPuffelen mentioned:

subscribing to topics on app start is fine.

However, I think it's also good to add in a subscribeToTopic() call in onTokenRefresh(), so that as soon as a new token is provided for the app instance, you'll immediately subscribe it accordingly.

Note that behavior I'm thinking here is that when a registration token is invalidated, it's subscription is also lost and that adding subscribeToTopic() in onRefreshToken() will immediately re-subscribe them for you (of course this still depends in your implementation on which topic you want to subscribe it).

Comments