一次性取消订阅Firebase Messaging中的所有主题

43

有没有一种方法可以一次性取消订阅所有主题?

我正在使用Firebase Messaging从一些已订阅的主题接收推送通知,但是我需要一次性取消订阅所有主题而不是一个一个地取消。这可能吗?


3
据我所知,目前没有一种“删除全部”API调用。 - tyczj
1
目前这是不可能的。 - Arthur Thompson
如果您检索所有主题的列表并循环遍历它,您是否会考虑使用“逐个”方式? - tobifasc
我在取消订阅FirebaseMessaging.unsubscribeFromTopic("new_updates")后仍然收到通知,但它不起作用,我还是收到通知。有其他方法可以取消单个用户的订阅吗?我做错了什么吗? - Mehul Gajjar
@MehulGajjar 你有检查过你的代码是否在其他时刻订阅了吗?如果没有,你可以尝试使用FirebaseInstanceId.getInstance().deleteInstanceId() - Lennon Spirlandelli
9个回答

22
您可以使用 Instance API 查询给定令牌已订阅的所有可用主题,然后调用多个请求以取消订阅所有主题。
但是,如果您想要停止接收来自所有主题的消息,那么该令牌也就不再有用了。此时,您可以调用 FirebaseInstanceId.getInstance().deleteInstanceId() (参考:deleteInstanceId()) 并将其包装在 try/catch 语句中以处理潜在的 IOException 异常。这将重置实例 ID,然后您可以使用新实例 ID 和令牌订阅新主题。
希望这能帮助到某些人。

在我的情况下,它不起作用,因为我没有存储任何“实例”。用户只能从订阅的主题中获取信息。 - Lennon Spirlandelli
@kirtan403 如果只有一个用户订阅了一个主题并删除了应用程序,那么他会从该主题中取消订阅吗?因此,如果有一个名为“weather”的主题(一个TextView)仅有一个用户,并且他取消订阅,则“weather”将不再是一个主题,而只是一个TextView,直到有其他人订阅为止,对吗? - Peter Haddad
@PeterHaddad 当您删除(卸载)应用程序时,实例ID将失效。因此,发送到该实例ID令牌的任何内容都不会被设备接收。如果没有订阅者订阅某个主题,则该主题也不存在了。 - kirtan403
@jobbert 我尝试使用 messaging.unsubscribeFromTopic("new_updates") 取消订阅,但仍然收到通知。有其他方法可以取消单个用户的订阅吗? - Mehul Gajjar
@kirtan403所说的是,您使用FirebaseMessaging生成了一个唯一的deviceToken实例ID,现在,所有主题都订阅了该特定实例ID,因为所有通知都通过该ID发送给用户。现在,如果每次用户登录时刷新此ID,或卸载并重新安装应用程序,则会获得新的实例ID,因此该设备ID中没有任何主题。 - Gastón Saillén
显示剩余6条评论

4

如果你想避免删除InstanceId,并且避免由于高概率的有缺陷实现而在本地数据库或远程数据库中保存时错过一些已订阅的主题,那么请先获取所有已订阅的主题:

    var options = BaseOptions(headers: {'Authorization':'key = YOUR_KEY'});
    var dio = Dio(options);
    var response = await dio.get('https://iid.googleapis.com/iid/info/' + token,
                             queryParameters: {'details': true});
    Map<String, dynamic> subscribedTopics = response.data['rel']['topics'];

在这里获取您的密钥: Firebase控制台 -> 您的项目 -> 项目设置 -> 云消息传递 -> 服务器密钥

获取您的令牌:

var firebaseMessaging = FirebaseMessaging();
String token;
firebaseMessaging.getToken().then((value) {
  token = value;
});

现在请取消订阅所有主题:

  Future<void> unsubscribeFromAllTopics() async {
    for (var entry in subscribedTopics.entries) {
      await Future.delayed(Duration(milliseconds: 100)); // throttle due to 3000 QPS limit
      unawaited(firebaseMessaging.unsubscribeFromTopic(entry.key)); // import pedantic for unawaited
      debugPrint('Unsubscribed from: ' + entry.key);
    }
  }

所有代码均使用Dart语言编写。

有关实例ID的更多信息: https://developers.google.com/instance-id/reference/server


想知道如果每秒限制为3000个查询,是否有必要将速度限制在100毫秒(每秒10个)。这样一来,取消订阅10个主题至少需要一秒钟的时间! - Tom Anderson
1
这是一个稳妥的赌注。你可以像往常一样调整数字 :) - Mutlu Simsek

3
我知道这不是最好的方法,但它是可行的!您可以将所有主题的列表存储在数据库中,然后在用户注销时取消订阅所有主题。
final FirebaseMessaging messaging= FirebaseMessaging.getInstance();
      FirebaseDatabase.getInstance().getReference().child("topics").addChildEventListener(new ChildEventListener() {
          @Override
          public void onChildAdded(DataSnapshot dataSnapshot, String s) {
              String topic  = dataSnapshot.getValue(String.class);
              messaging.unsubscribeFromTopic(topic);
}...//rest code

在我看来,这是正确且最简单的解决方案。只需要将所有订阅主题列表保存到首选项或其他地方,然后循环一次性取消订阅它们。 - Taufik Nur Rahmanda

2

在“首选项”中保留一个私人订阅主题列表。

这并不难。以下是我的做法:

public class PushMessagingSubscription {

  private static SharedPreferences topics;

  public static void init(ApplicationSingleton applicationSingleton) {
    topics = applicationSingleton.getSharedPreferences("pushMessagingSubscription", 0);
  }

  public static void subscribeTopic(String topic) {
    if (topics.contains(topic)) return; // Don't re-subscribe
    topics.edit().putBoolean(topic, true).apply();

    // Go on and subscribe ...
  }


  public static void unsubscribeAllTopics() {
    for (String topic : topics.getAll().keySet()) {
      FirebaseMessaging.getInstance().unsubscribeFromTopic(topic);
    }
    topics.edit().clear().apply();
    // FirebaseInstanceId.getInstance().deleteInstanceId();
  }
}

2

针对Java用户:

如果你想按主题分类进行操作,请参考其他答案。如果你想停止接收FCM推送通知,请执行以下操作:

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            FirebaseInstanceId.getInstance().deleteInstanceId();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}).start();

我已将deleteInstanceId()放置在单独的线程中,以防止java.io.IOException: MAIN_THREAD W/System.err错误,并用try / catch包装以处理IOException异常。


2
Firebase.messaging.deleteToken()

实际答案为2021年。


1
需要使用POST。我认为GET不会起作用。 - Vitalicus

0

如果你正在使用Flutter,如果想要取消订阅所有主题,请使用-

  final FirebaseMessaging _fcm = FirebaseMessaging();
  _fcm.deleteInstanceID().then((value){
                print('deleted all'+value.toString());
              });

0
我们可以在服务器端使用unsubscribeAllTopics如下。
例如,
interface GetTopics {
  rel: {topics: {[key: string]: any}}
}

/**
 * Unsubcribe all topics except one topic
 * 
 * Example response of `https://iid.googleapis.com/iid/info/${fcmToken}?details=true`
 * {
    "applicationVersion": "...",
    "application": "...",
    "scope": "*",
    "authorizedEntity": "...",
    "rel": {
        "topics": {
            "TOPIC_KEY_STRING": { // topic key
                "addDate": "2020-12-23"
            }
        }
    },
    "appSigner": "...",
    "platform": "ANDROID"
}
 */
export const unsubcribeAllTopics = async (
  fcmTokens: string | string[],
  exceptTopic?: string,
) => {
  const headers = {
    'Content-Type': 'application/json',
    Authorization: `key=${process.env.FCM_SERVER_KEY}`,
  }

  const url = `https://iid.googleapis.com/iid/info/${fcmTokens}?details=true`

  try {
    const response = await fetch(url, {method: 'GET', headers: headers})
    const result: GetTopics = await response.json()
    const keys = Object.keys(result.rel.topics)

    keys.forEach(key => {
      key !== exceptTopic &&
        messaging()
          .unsubscribeFromTopic(fcmTokens, key)
          .catch(error => {
            console.error('error', {data: error})
          })
    })
  } catch (error) {
    console.error('error', {data: error})
  }
}

https://gist.github.com/JeffGuKang/62c280356b5632ccbb6cf146e2bc4b9d


-2
try {
    FirebaseInstallations.getInstance().delete()
} catch (e: IOException) {

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接