将注册按钮放在片段中的什么位置(对 Firebase Realtime DB 的异步 DB 调用太慢会导致应用程序崩溃)

Where do I put the signup buttons in a Fragment (Async DB call to Firebase Realtime DB is too slow makes app crash)

我发誓我搜索了很多但最后我投降了,我要问了。我正在制作一个只有一个 Activity 的应用程序,所有内容都通过 Fragments 进行管理,一切正常,布局设置良好,但我在注册时遇到问题。

我有 3 种注册方式,邮箱和密码,Facebook 和 Google。 电子邮件注册方法:

 private void emailSignUp() {

    String email = etEmail.getText().toString();
    String password = etPassword.getText().toString();
    if(email.isEmpty()||password.isEmpty()) {
        Toast.makeText(getActivity(), "Empty fields are not allowed.",
                Toast.LENGTH_SHORT).show();
        return;
    }

    mAuth.createUserWithEmailAndPassword(email, password)
            .addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        // Sign in success, update UI with the signed-in user's information
                        Log.d(TAG, "signInWithCredential:success");
                        user = mAuth.getCurrentUser();
                        //Read DB Score
                        database = FirebaseDatabase.getInstance();
                        database.getReference(user.getUid())
                                .addListenerForSingleValueEvent(new ValueEventListener() {
                                    @Override
                                    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                                        if(!dataSnapshot.exists()) database.getReference(user.getUid()).setValue(0);
                                        updateUI(user);
                                    }

                                    @Override
                                    public void onCancelled(@NonNull DatabaseError databaseError) {

                                    }
                                });
                    } else {
                        // If sign in fails, display a message to the user.
                        Log.w(TAG, "createUserWithEmail:failure", task.getException());
                        Toast.makeText(getActivity(), "Authentication failed.",
                                Toast.LENGTH_SHORT).show();
                    }
                }
            });
}

其他2个相同;这些方法是通过使用 onClickListener 从 onActivityCreated 调用的,该方法工作正常。

如您所见,这些方法中的每一个都使用刚刚创建的用户的 UID 创建了一个 DB 值,并将其设置为 0。到这里为止一切正常。 如果用户不为空,有一个 updateUI 方法负责移动 Fragment,下一个 Fragment 是 Dashboard,这里我从服务器获取数据有问题: 每次我注册一个新用户时,我在调用数据库时都会得到一个空对象引用,因为先例片段中数据库键的异步创建仍未完成,如果我重新启动应用程序,仪表板片段可以完美运行,因为值为那里。

在仪表板片段中,我正在从 onStart 获取数据库数据。

我几乎完全确定我的代码中的错误是在 Dashboard Fragment 中放置按钮调用或 fetchData,我尝试了解 Fragment 生命周期,但如果调用异步任务则无法理解。

有什么帮助吗?

P.S.: 每个对象或值都在 Fragment 代码中设置为私有。

按要求错误:

2019-11-25 20:16:04.403 3342-3342/com.d136.uselessrank E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.d136.uselessrank, PID: 3342
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.isEmpty()' on a null object reference
    at com.d136.uselessrank.fragments.fragmentDashboard.getName(fragmentDashboard.java:142)
    at com.d136.uselessrank.fragments.fragmentDashboard.onStart(fragmentDashboard.java:67)
    at androidx.fragment.app.Fragment.performStart(Fragment.java:2632)
    at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:915)
    at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
    at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
    at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
    at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
    at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
    at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
    at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
    at androidx.fragment.app.FragmentManagerImpl.run(FragmentManagerImpl.java:150)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6119)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

和片段仪表板:

    private void getName() {
    if(currentUser.getDisplayName().isEmpty()) email.setText(currentUser.getEmail());
    else email.setText(currentUser.getDisplayName());
}

从同一个 FragmentDashboard 调用:

    @Override
public void onStart() {
    super.onStart();
    getName();
    getProfileImage();
    readScore();
}

currentUser 它肯定是 null,问题是当调用 currentUser 时(OnActivityCreated)片段仍然没有完成填充该 db 值。我如何让它等待它?

感谢@Frank van Puffelen,在我实现的 Fragment Dashboard 中解决了:

    @Override
public void onStart() {
    super.onStart();
    FirebaseAuth.getInstance().addAuthStateListener(new FirebaseAuth.AuthStateListener() {
        @Override
        public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
            mAuth = FirebaseAuth.getInstance();
            currentUser = mAuth.getCurrentUser();
            updateUI(currentUser);
        }
    });

}

稍加调试后一切顺利。

当您创建新用户时,AuthResult 中的用户对象将被正确填充。但是,FirebaseAuth.getInstance().getCurrentUser() 是异步填充的。

通常这不是问题,但如果您转换到新的 activity/fragment,您还不能指望 FirebaseAuth.getInstance().getCurrentUser() 被填充。

这里有两个选择:

  1. authResult.getUser() 的值放在共享首选项中,并在下一个 activity/fragment 中用作当前用户。
  2. 在接下来的activity/fragment中用FirebaseAuth.getInstance().addAuthStateListener(listener)附加一个auth state listener,等待用户在那里初始化。

    FirebaseAuth.getInstance().addAuthStateListener(new  FirebaseAuth.AuthStateListener() {
        @Override
        public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
            FirebaseUser user = firebaseAuth.getCurrentUser();
            if (user != null) {
                // User is signed in
                Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
            } else {
                // User is signed out
                Log.d(TAG, "onAuthStateChanged:signed_out");
            }
            // ...
        }
    });