You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Firebase关联同邮箱异提供商账户时遇空指针异常求助

解决Firebase Auth中同邮箱不同提供商账户关联的空指针问题

首先得明确你遇到空指针的核心原因:当触发FirebaseAuthUserCollisionException时,当前并没有已登录的用户——因为你用Facebook凭证登录的操作失败了,mAuth.getCurrentUser()自然返回null,直接调用它的linkWithCredential()肯定会抛空指针。

正确的处理逻辑应该是:先找到关联该邮箱的现有账户,引导用户登录到这个账户后,再把新的提供商凭证(比如你的Facebook凭证)链接到已登录的账户上。

具体解决方案步骤

1. 捕获异常时提取冲突邮箱,查询已关联的提供商

当捕获到账户冲突异常时,先从异常里取出对应的邮箱地址,再调用Firebase Auth的fetchSignInMethodsForEmail()方法,获取该邮箱已经绑定的登录提供商(比如Google)。

2. 引导用户用已关联的提供商登录

根据查询到的提供商类型,提示用户用对应的方式登录(比如“该邮箱已绑定Google账号,请先登录Google账号”),等用户登录成功后,再执行凭证链接操作。

3. 调整后的代码实现

下面是修改后的完整代码示例:

首先更新handleFacebookAccessToken的异常处理逻辑:

public void handleFacebookAccessToken(final AccessToken token, final Activity activity) {
    Log.d(TAG, "handleFacebookAccessToken: " + token);
    final AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
    mAuth.signInWithCredential(credential)
        .addOnCompleteListener(activity, new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Log.d(TAG, "signInWithCredential:success");
                    FirebaseUser user = mAuth.getCurrentUser();
                    authListener.updateSignedUI(user);
                }
            }
        }).addOnFailureListener(activity, new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Log.w(TAG, "signInWithCredential:failure", e);
                if (e instanceof FirebaseAuthUserCollisionException) {
                    // 从异常中提取冲突邮箱
                    String conflictEmail = ((FirebaseAuthUserCollisionException) e).getEmail();
                    // 处理账户关联逻辑
                    handleAccountConflict(conflictEmail, credential, activity);
                }
            }
        });
}

然后实现handleAccountConflict和链接凭证的方法:

private void handleAccountConflict(String email, AuthCredential newCredential, Activity activity) {
    // 查询该邮箱已绑定的登录方式
    mAuth.fetchSignInMethodsForEmail(email)
        .addOnCompleteListener(new OnCompleteListener<SignInMethodQueryResult>() {
            @Override
            public void onComplete(@NonNull Task<SignInMethodQueryResult> task) {
                if (task.isSuccessful()) {
                    SignInMethodQueryResult result = task.getResult();
                    List<String> linkedProviders = result.getSignInMethods();
                    if (linkedProviders != null && !linkedProviders.isEmpty()) {
                        // 这里可以根据返回的提供商提示用户登录
                        // 比如如果包含GoogleAuthProvider.PROVIDER_ID,就引导用户登录Google账号
                        // 假设用户完成登录后,调用链接方法
                        linkNewCredentialToCurrentUser(newCredential, activity);
                    }
                } else {
                    Log.w(TAG, "fetchSignInMethodsForEmail:failure", task.getException());
                }
            }
        });
}

private void linkNewCredentialToCurrentUser(AuthCredential credential, Activity activity) {
    FirebaseUser currentUser = mAuth.getCurrentUser();
    if (currentUser != null) {
        currentUser.linkWithCredential(credential)
            .addOnCompleteListener(activity, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "linkWithCredential:success");
                        FirebaseUser user = task.getResult().getUser();
                        authListener.updateSignedUI(user);
                    } else {
                        Log.w(TAG, "linkWithCredential:failure", task.getException());
                        authListener.updateSignedUI(null);
                    }
                }
            });
    } else {
        // 提示用户先登录已绑定的账户
        Log.w(TAG, "请先登录该邮箱已关联的账号");
        // 这里可以跳转到登录选择页面,让用户选择对应的提供商登录
    }
}

关键注意事项

  • 永远不要默认mAuth.getCurrentUser()不为null,尤其是登录失败的回调里,必须先做非空判断。
  • fetchSignInMethodsForEmail()返回的linkedProviders列表包含了该邮箱绑定的所有提供商ID(比如google.comfacebook.com),可以根据这些值做精准的登录引导。
  • 只有用户登录到现有账户后,才能执行linkWithCredential()操作,此时currentUser才会是非空的,不会再抛出空指针异常。

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

火山引擎 最新活动