Firebase云消息传递 - 处理注销

Mic*_*ł K 59 android firebase firebase-cloud-messaging

当用户退出我的应用程序并且我不再希望他接收到设备的通知时,我该如何处理这种情况.

我试过了

FirebaseInstanceId.getInstance().deleteToken(FirebaseInstanceId.getInstance().getId(), FirebaseMessaging.INSTANCE_ID_SCOPE)
Run Code Online (Sandbox Code Playgroud)

但我仍然收到我的设备的通知registration_id.

我还确保这是我应删除的令牌:

FirebaseInstanceId.getInstance().getToken(FirebaseInstanceId.getInstance().getId(), FirebaseMessaging.INSTANCE_ID_SCOPE)
Run Code Online (Sandbox Code Playgroud)

或者简单地说FirebaseInstanceId.getInstance().getToken()).

我也尝试过FirebaseInstanceId.getInstance().deleteInstanceId(),但是下次我打电话给FirebaseInstanceId.getInstance.getToken我时会收到空(它在第二次尝试时起作用).

我想,在deleteInstanceId我能够立即getToken()再次打电话之后,它看起来像是一个黑客.并且还有这个答案表明不应该这样做,但它建议删除显然不起作用的令牌.

那么处理这个问题的正确方法是什么?

AL.*_*AL. 45

好的.所以我设法做了一些测试并得出以下结论:

  1. deleteToken()是对应的getToken(String, String),但不是getToken().

仅当您传递的发件人ID是不同的发件人ID(与您在google-services.json中可以看到的ID不同)时,它才有效.例如,您希望允许其他服务器发送到您的应用,您可以致电授权getToken("THEIR_SENDER_ID", "FCM")他们发送到您的应用.这将返回一个不同的注册令牌,该令牌仅对应于该特定发件人.

将来,如果您选择删除授权发送到您的应用,那么您将不得不使用deleteToken("THEIR_SENDER_ID", "FCM").这将使相应的令牌无效,并且当发件人尝试发送消息时,作为预期的行为,他们将收到NotRegistered错误.

  1. 要删除您自己的发件人的令牌,请使用正确的处理方法deleteInstanceId().

特别提到@Prince的这个答案,特别是帮助我解决这个问题的代码示例.

正如@MichałK已经在他的帖子中做的那样,在调用之后deleteInstanceId(),getToken()应该调用它来发送一个新令牌的请求.但是,您不必第二次调用它.只要实现,它应该自动触发为您提供新令牌.onTokenRefresh() onNewToken()

对于短,deleteInstanceId()> getToken()>检查.onTokenRefresh() onNewToken()

注意:调用deleteInstanceId()不仅会删除您自己应用的令牌.它将删除所有主题订阅以及与应用程序实例关联的所有其他令牌.


你肯定你说得对deleteToken()吗?观众的价值应该(也可以从我链接的答案中看出)"设置为应用服务器的发件人ID".您传递的getId()值与发件人ID不同(它包含应用程序实例ID值).另外,您如何发送消息(App Server或Notifications Console)?

getToken()getToken(String, String)返回不同的标记.在这里看到我的答案.

我也尝试过FirebaseInstanceId.getInstance().deleteInstanceId(),但是下次我打电话给FirebaseInstanceId.getInstance.getToken我时会收到空(它在第二次尝试时起作用).

这可能是因为你第一次打电话时getToken(),它仍在生成.这只是预期的行为.

我想,在deleteInstanceId我能够立即getToken()再次打电话之后,它看起来像是一个黑客.

并不是的.这是你如何获得新生成的(如果它已经生成)令牌.所以我认为没关系.

  • 有没有办法在离线时停止侦听 FCM,因为在这种情况下 deleteInstanceId 将返回 SERVICE_NOT_AVAILABLE_ERROR,并且您仍将被注册 (4认同)
  • 谢谢你查一下!老实说,我很惊讶这个API有多糟糕和记录不好......但是,请给它一个旋转,谢谢! (2认同)

Sun*_*nil 19

当我logout()从我的应用程序中完成我的工作时,我正在处理同样的问题.但问题是,在退出后,我仍然从Firebase获得推送通知.我尝试删除Firebase令牌.但是在我的logout()方法中删除令牌后,我在我的方法中null查询它login().工作2天后,我终于找到了解决方案.

  1. 在您的logout()方法中,请在后台删除Firebase令牌,因为您无法从主线程中删除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();
    
    Run Code Online (Sandbox Code Playgroud)
  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();
    
    Run Code Online (Sandbox Code Playgroud)

  • while(token == null) { busy loop } 对你的电池真的不好......最好是注册一个回调。另一个(不好但更好)是在等待令牌时使用 Thread.sleep。 (2认同)

Ján*_*iár 19

我做了一个简短的研究,讨论如何恢复完全控制(订阅和取消订阅FCM)的最优雅的解决方案.用户登录或注销后启用和禁用FCM.

步骤1. - 防止自动初始化

Firebase现在可以处理InstanceID生成注册令牌所需的所有其他内容.首先,您需要阻止自动初始化.根据官方设置文档,您需要将这些元数据值添加到您的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>
Run Code Online (Sandbox Code Playgroud)

现在您禁用了自动令牌请求过程.同时,您可以选择在运行时通过代码再次启用它.

第2步 - 实施enableFCM()disableFCM()功能

如果再次启用自动初始化,则会立即收到新令牌,因此这是实现该enableFCM()方法的完美方式.所有订阅信息都分配给InstanceID,因此当您删除它时,然后启动以取消订阅所有主题.通过这种方式,您可以实现disableFCM()方法,只需在删除之前关闭自动初始化.

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

}
Run Code Online (Sandbox Code Playgroud)

第3步 - FCMService实现 - 令牌和消息接收

在最后一步中,您需要接收新令牌并直接发送到您的服务器.另一方面,您将收到您的数据信息,并按照您的要求进行操作.

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
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为这个解决方案清晰,简单,透明.我在生产环境中进行了测试,但它确实有效.我希望它有所帮助.


小智 8

由于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();
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*anu 7

由于以下原因,开发人员永远不应注销客户端应用程序作为注销或在用户之间切换的机制:

  • 注册令牌与特定的登录用户无关。如果客户端应用取消注册然后重新注册,则该应用可以接收相同的注册令牌或不同的注册令牌。
  • 注销和重新注册可能最多需要五分钟才能传播。在此期间,由于未注册状态,消息可能会被拒绝,并且消息可能发送给错误的用户。为确保将消息发送给目标用户,请执行以下操作:

  • 应用服务器可以维护当前用户和注册令牌之间的映射。

  • 然后,客户端应用程序可以检查以确保收到的消息与登录用户匹配。

此报价来自已弃用的Google文档

但是有理由相信这仍然是正确的-即使上述文档已弃用。

您可以在此处进行观察-在此代码实验室中查看他们的操作方式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


jak*_*ang 5

使用这个方法。这是我的解决方案,我在这里提到了 当您注册时,使用 initFirebaseMessage。当注销或删除时使用removeFirebaseMessage()。

    private fun removeFirebaseMessage(){
        CoroutineScope(Dispatchers.Default).launch {
            FirebaseMessaging.getInstance().isAutoInitEnabled = false
            FirebaseInstallations.getInstance().delete()
            FirebaseMessaging.getInstance().deleteToken()
        }
    }

    private fun initFirebaseMessage(){
        val fcm = FirebaseMessaging.getInstance()
        fcm.isAutoInitEnabled = true
        fcm.subscribeToTopic("all")
        fcm.subscribeToTopic("")
    }
Run Code Online (Sandbox Code Playgroud)