web-dev-qa-db-ja.com

Firebase Cloud Messaging-ログアウトの処理

ユーザーがアプリケーションからログアウトし、デバイスへの通知を受信する必要がなくなった場合の対処方法。

私は試した

FirebaseInstanceId.getInstance().deleteToken(FirebaseInstanceId.getInstance().getId(), FirebaseMessaging.INSTANCE_ID_SCOPE)

ただし、デバイスのregistration_idへの通知は引き続き受信します。

また、これが削除するトークンであることを確認しました。

FirebaseInstanceId.getInstance().getToken(FirebaseInstanceId.getInstance().getId(), FirebaseMessaging.INSTANCE_ID_SCOPE)

または単にFirebaseInstanceId.getInstance().getToken())。

FirebaseInstanceId.getInstance().deleteInstanceId()も試しましたが、次にFirebaseInstanceId.getInstance.getTokenを呼び出したときにnullを受け取ります(2回目の試行で機能します)。

deleteInstanceIdの後で、すぐにgetToken()を再び呼び出すことができたと思いますが、ハックのように見えます。また、 この答え があります。これは、実行すべきではないことを示していますが、明らかに機能しないトークンを削除することを提案しています。

それで、これを処理する正しい方法は何ですか?

56
Michał K

はい。そこで、なんとかテストを行って、次のことを結論付けました。

  1. deleteToken()getToken(String, String)に対応していますが、getToken()には対応していません。

渡すSender IDが別のSender ID(google-services.jsonで確認できる同じIDではない)である場合にのみ機能します。たとえば、別のサーバーからアプリへの送信を許可するには、getToken("THEIR_SENDER_ID", "FCM")を呼び出して、アプリに送信するauthorizationを付与します。これにより、その特定の送信者のみに対応する別の登録トークンが返されます。

将来、それらのauthorizationを削除してアプリに送信することを選択した場合は、deleteToken("THEIR_SENDER_ID", "FCM")を使用する必要があります。これにより、対応するトークンが無効になり、送信者が意図した動作としてメッセージを送信しようとすると、NotRegisteredエラーを受け取ります。

  1. 独自の送信者のトークンを削除するには、deleteInstanceId()を使用するのが正しい処理です。

これについて特記してください @ Princeによる回答 、特にこれを手伝ってくれるコードサンプル.

@MichałKがすでに投稿で行っているように、deleteInstanceId()を呼び出した後、新しいトークンのリクエストを送信するには、getToken()を呼び出す必要があります。ただし、2回目に呼び出す必要はありません。限り onTokenRefresh() onNewToken()が実装され、新しいトークンを提供して自動的にトリガーされるはずです。

要するに、deleteInstanceId()> getToken()>チェック onTokenRefresh() onNewToken()

deleteInstanceId()を呼び出すと、独自のアプリのトークンが削除されるだけではありません。すべてのトピックサブスクリプションと、アプリインスタンスに関連付けられている他のすべてのトークンを削除します。


deleteToken()を適切に呼び出していると確信していますか?オーディエンスの値(リンクした回答からもわかる)は、「アプリサーバーの送信者IDに設定」です。 Sender IDとは異なるgetId()値を渡します(アプリインスタンスID値が含まれます)。また、メッセージ(App ServerまたはNotifications Console)をどのように送信していますか?

getToken()getToken(String, String)は異なるトークンを返します。私の答えをご覧ください こちら

私もFirebaseInstanceId.getInstance().deleteInstanceId()を試しましたが、次にFirebaseInstanceId.getInstance.getTokenを呼び出したときにnullを受け取ります(2回目の試行で動作します)。

おそらく、getToken()を初めて呼び出したとき、まだ生成されているためです。これは意図した動作です。

deleteInstanceIdの後、すぐにgetToken()を再び呼び出すことができたと思いますが、ハックのように見えます。

あんまり。新しいトークン(既に生成されている場合)を取得する方法です。だから大丈夫だと思う。

42
AL.

私がアプリケーションからlogout()を実行したとき、私は同じ問題に取り組んでいました。しかし問題は、ログアウトした後でも、Firebaseからプッシュ通知を受け取っていたことでした。 Firebaseトークンを削除するを試みました。しかし、logout()メソッドでトークンを削除した後、login()メソッドでトークンを照会すると、nullになります。 2日間働いた後、私は最終的に解決策を得た。

  1. メインスレッドからFirebaseトークンを削除できないため、logout()メソッドでバックグラウンドでFirebaseトークンを削除します。

    new AsyncTask<Void,Void,Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            try
            {
                FirebaseInstanceId.getInstance().deleteInstanceId();
            } catch (IOException e)
            {
                e.printStackTrace();
            }
            return null;
        }
    
        @Override
        protected void onPostExecute(Void result) {
            // Call your Activity where you want to land after log out
        }
    }.execute();
    
  2. login()メソッドで、Firebaseトークンを再度生成します。

    new AsyncTask<Void,Void,Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            String token = FirebaseInstanceId.getInstance().getToken();
            // Used to get firebase token until its null so it will save you from null pointer exeption
            while(token == null) {
                token = FirebaseInstanceId.getInstance().getToken();
            }
            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
        }
    }.execute();
    
18
Sunil

以前と同様に、フルコントロール(FCMへのサブスクライブとサブスクライブ解除)を取り戻すための最もエレガントなソリューションを簡単に調査しました。ユーザーがログインまたはログアウトした後、FCMを有効または無効にします。

ステップ1-自動初期化を防止する

FirebaseはInstanceIDおよび登録トークンを生成する必要がある他のすべてを処理するようになりました。まず、自動初期化を防ぐ必要があります。 公式セットアップドキュメント に基づいて、これらのmeta-data値をAndroidManifest.xmlに追加する必要があります。

<?xml version="1.0" encoding="utf-8"?>
<application>

  <!-- FCM: Disable auto-init -->
  <meta-data Android:name="firebase_messaging_auto_init_enabled"
             Android:value="false" />
  <meta-data Android:name="firebase_analytics_collection_enabled"
             Android:value="false" />

  <!-- FCM: Receive token and messages -->
  <service Android:name=".FCMService">
    <intent-filter>
        <action Android:name="com.google.firebase.MESSAGING_EVENT"/>
    </intent-filter>
  </service>

</application>

これで、自動トークンリクエストプロセスが無効になりました。同時に、実行時にコードによって再度有効にするオプションがあります。

ステップ2.-enableFCM()およびdisableFCM()関数を実装します

自動初期化を再度有効にすると、すぐに新しいトークンを受信するため、これはenableFCM()メソッドを実装するのに最適な方法です。 InstanceIDに割り当てられたすべてのサブスクライブ情報。したがって、削除すると、すべてのトピックのサブスクライブ解除が開始されます。この方法でdisableFCM()メソッドを実装できるので、削除する前にauto-initをオフに戻すだけです。

public class FCMHandler {

    public void enableFCM(){
        // Enable FCM via enable Auto-init service which generate new token and receive in FCMService
        FirebaseMessaging.getInstance().setAutoInitEnabled(true);
    }

    public void disableFCM(){
        // Disable auto init
        FirebaseMessaging.getInstance().setAutoInitEnabled(false);
        new Thread(() -> {
            try {
                // Remove InstanceID initiate to unsubscribe all topic
                // TODO: May be a better way to use FirebaseMessaging.getInstance().unsubscribeFromTopic()
                FirebaseInstanceId.getInstance().deleteInstanceId();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }

}

ステップ3-FCMService implementation-トークンとメッセージの受信

最後のステップでは、新しいトークンを受け取り、サーバーに直接送信する必要があります。もう一方の手では、データメッセージを受け取り、必要なだけ実行します。

public class FCMService extends FirebaseMessagingService {

    @Override
    public void onNewToken(String token) {
        super.onNewToken(token);
        // TODO: send your new token to the server
    }

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);
        String from = remoteMessage.getFrom();
        Map data = remoteMessage.getData();
        if (data != null) {
            // TODO: handle your message and data
            sendMessageNotification(message, messageId);
        }
    }

    private void sendMessageNotification(String msg, long messageId) {
        // TODO: show notification using NotificationCompat
    }
}

この解決策は明確でシンプルで透明だと思います。実稼働環境でテストしましたが、動作します。お役に立てば幸いです。

17

開発者は、次の理由により、ログアウトまたはユーザー間の切り替えのメカニズムとしてクライアントアプリを登録解除しないでください。

  • 登録トークンは、特定のログインユーザーに関連付けられていません。クライアントアプリの登録を解除してから再登録すると、アプリは同じ登録トークンまたは異なる登録トークンを受信できます。
  • 登録解除と再登録は、それぞれ伝播するのに最大5分かかります。この間、メッセージは登録されていない状態のために拒否され、メッセージは間違ったユーザーに送られる可能性があります。メッセージが目的のユーザーに送信されることを確認するには:

  • アプリサーバーは、現在のユーザーと登録トークンの間のマッピングを維持できます。

  • クライアントアプリは、受信したメッセージがログインユーザーと一致することを確認できます。

これは廃止されたGoogleドキュメントからのものでした: https://developers.google.com/cloud-messaging/registration#unregistration-and-unsubscription

しかし、これがまだ利用可能であると信じる理由があります

このFirebaseコードを確認してください https://github.com/firebase/functions-samples/blob/master/fcm-notifications/functions/index.js

そしてこれ https://github.com/firebase/friendlychat-web/blob/master/cloud-functions/public/scripts/main.js

5
Dan Alboteanu

getToken()非推奨なので、代わりにgetInstanceId()を使用して新しいトークンを再生成します。同じ効果があります。

public static void resetInstanceId() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                FirebaseInstanceId.getInstance().deleteInstanceId();
                FirebaseInstanceId.getInstance().getInstanceId();   
                Helper.log(TAG, "InstanceId removed and regenerated.");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
}
4
Reedy Creeker

私はパーティーに遅れていることを知っています。 deleteInstanceId()はブロッキング呼び出しなので、バックグラウンドスレッドから呼び出す必要があります。 FirebaseInstanceId() クラスのdeleteInstanceId()メソッドを確認するだけです。

@WorkerThread
public void deleteInstanceId() throws IOException {
    if (Looper.getMainLooper() == Looper.myLooper()) {
        throw new IOException("MAIN_THREAD");
    } else {
        String var1 = zzh();
        this.zza(this.zzal.deleteInstanceId(var1));
        this.zzl();
    }
}  

IntentServiceを開始して、インスタンスIDとそれに関連付けられたデータを削除できます。

2