将注册按钮放在片段中的什么位置(对 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()
被填充。
这里有两个选择:
- 将
authResult.getUser()
的值放在共享首选项中,并在下一个 activity/fragment 中用作当前用户。
在接下来的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");
}
// ...
}
});
我发誓我搜索了很多但最后我投降了,我要问了。我正在制作一个只有一个 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()
被填充。
这里有两个选择:
- 将
authResult.getUser()
的值放在共享首选项中,并在下一个 activity/fragment 中用作当前用户。 在接下来的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"); } // ... } });