You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase Android:生成独立于设备的用户唯一Token问题求助

解决Firebase Auth注册时同一设备用户Token重复的问题

嘿,我完全懂你的困扰——你现在碰到的核心问题是混淆了两种不同的Token:你之前用的FirebaseInstanceId(现在已经被官方弃用)生成的是设备级的FCM推送令牌,它是绑定设备而非用户的,所以同一设备上不管注册多少用户,这个Token都会一样。而你需要的是和用户绑定的、能用来给特定用户发通知的关联方案,毕竟一个用户可能在多台设备登录。

正确的解决方案思路

要实现给特定用户发定时通知,你需要做的是:

  • 获取当前设备的FCM推送令牌(取代旧的InstanceId)
  • 将这个令牌与用户的UID绑定,存储到Firestore数据库中(比如给用户文档加一个数组,存储该用户所有设备的令牌)
  • 后续发送通知时,根据用户UID从数据库取出对应的所有令牌,调用FCM API发送即可

修改后的代码示例

首先确保你已经引入了Firebase Messaging的依赖,然后把注册逻辑改成这样:

mAuth.createUserWithEmailAndPassword(email, pswd)
    .addOnSuccessListener(new OnSuccessListener<AuthResult>() {
        @Override
        public void onSuccess(AuthResult authResult) {
            final FirebaseUser mFirebaseUser = authResult.getUser();
            if (mFirebaseUser == null) {
                Toast.makeText(getApplicationContext(), "Une erreur inconnue s'est produite", Toast.LENGTH_SHORT).show();
                return;
            }

            String uid = mFirebaseUser.getUid();
            Log.d(TAG, "UID DU USER: " + uid);

            // 获取FCM推送令牌(取代废弃的FirebaseInstanceId)
            FirebaseMessaging.getInstance().getToken()
                .addOnCompleteListener(new OnCompleteListener<String>() {
                    @Override
                    public void onComplete(@NonNull Task<String> task) {
                        if (!task.isSuccessful()) {
                            Log.w(TAG, "获取FCM令牌失败", task.getException());
                            Toast.makeText(getApplicationContext(), "获取通知令牌失败", Toast.LENGTH_SHORT).show();
                            return;
                        }

                        // 获取到的设备级FCM令牌
                        String fcmToken = task.getResult();
                        Log.d(TAG, "FCM令牌: " + fcmToken);

                        // 准备用户数据,注意这几个关键点:
                        // 1. 绝对不要存储明文密码!Firebase Auth已经帮你安全管理密码了
                        // 2. 用数组存储令牌,支持用户多设备登录时都能收到通知
                        Map<String, Object> userdata = new HashMap<>();
                        userdata.put("uid", uid);
                        userdata.put("fln", fln);
                        userdata.put("email", email);
                        userdata.put("fcmTokens", Arrays.asList(fcmToken));

                        // 更新本地SharedPreferences
                        SharedPreferences.Editor editor = prefEnreg.edit();
                        editor.putString("FULLNAME", fln);
                        editor.putString("tok", fcmToken);
                        editor.apply();

                        progressBar.setVisibility(View.VISIBLE);
                        register.setVisibility(View.GONE);

                        // 存储到Firestore,用merge避免覆盖用户后续添加的其他设备令牌
                        db.collection("Users2").document(uid)
                            .set(userdata, SetOptions.merge())
                            .addOnCompleteListener(new OnCompleteListener<Void>() {
                                @Override
                                public void onComplete(@NonNull Task<Void> task) {
                                    progressBar.setVisibility(View.GONE);
                                    register.setVisibility(View.VISIBLE);
                                    if (task.isSuccessful()) {
                                        Toast.makeText(getApplicationContext(), "Register User", Toast.LENGTH_SHORT).show();
                                        Intent intent13 = new Intent(MainActivity.this, FriendList.class);
                                        startActivity(intent13);
                                        // 清空输入框
                                        mail.setText("");
                                        phone.setText("");
                                        fllname.setText("");
                                        passwd.setText("");
                                    } else {
                                        Toast.makeText(getApplicationContext(), "存储用户数据失败: " + task.getException().getMessage(), Toast.LENGTH_SHORT).show();
                                    }
                                }
                            });
                    }
                });
        }
    })
    .addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            Toast.makeText(getApplicationContext(), "注册失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    });

额外的重要注意事项

  • 不要存储明文密码:你的原代码里把用户密码存在Firestore里,这是严重的安全漏洞,Firebase Auth已经提供了安全的密码管理机制,绝对不要自己存储明文密码。
  • FCM令牌可能刷新:设备令牌会在应用卸载重装、系统更新、应用数据清除等场景下刷新,建议在用户每次登录时,都检查当前令牌是否已经在用户的fcmTokens数组中,如果不在就添加进去,避免通知无法送达。
  • Auth ID Token的用途:你之前调用的getIdToken(true)获取的是Firebase Auth的身份验证令牌,这个Token用于验证用户身份(比如调用你的后端API时),不能用来发送FCM通知,发送通知必须用FCM推送令牌。
  • 定时通知的实现:当你需要给特定用户发定时通知时,只需从Firestore中取出该用户的fcmTokens数组,遍历每个令牌,调用FCM的发送通知API即可。

内容的提问来源于stack exchange,提问作者Rolanddev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 17:37:38