Issue
I swear I searched a lot but in the end I'm surrendering, I'm going to ask. I'm making an app where there is only one Activity and everything is managed through Fragments, everything works perfectly, layout is well setup, but I have a problem with the signup.
I have 3 types of signup, mail and password, Facebook and Google. Emailsignup method:
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();
}
}
});
}
The other 2 are the same; the methods are called from onActivityCreated trough the use of onClickListener, the method works correctly.
As you can see, everyone of those methods creates a DB value with the UID of the user just created, and it sets it up to 0. Everything works perfectly up to here. There is an updateUI method which is responsible to move Fragment if user is not null, the next Fragment is a Dashboard and here I have a problem with the data fetch from the server: every time I register a new user I get a null object reference when I call the db because the async creation of the db key in the precedent fragment has still not finished, if I restart the app, the dashboard fragment works perfectly because the value is there.
In the dashboard Fragment I'm fetching DB data from onStart.
I'm almost completely sure that what's wrong in my code is the placing of the button calls or the fetchData in the Dashboard Fragment, I tried understanding Fragment lifecycle but can't understand it if async tasks are called.
Any help?
P.S.: Every object or value is set up as private in the Fragment code.
As requested the error:
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$2.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)
and Fragment Dashboard:
private void getName() {
if(currentUser.getDisplayName().isEmpty()) email.setText(currentUser.getEmail());
else email.setText(currentUser.getDisplayName());
}
that is called from the same FragmentDashboard:
@Override
public void onStart() {
super.onStart();
getName();
getProfileImage();
readScore();
}
currentUser it's null for sure, the problem is that when currentUser is called (OnActivityCreated) the fragment still has not finished populating that db value. How do I make it wait for it?
Solved thanks to @Frank van Puffelen, in Fragment Dashboard I implemented:
@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);
}
});
}
and after a little debugging everything went smoothly.
Solution
When you create a new user, the user object in the AuthResult
will be populated correctly. However, the FirebaseAuth.getInstance().getCurrentUser()
is populated asynchronously.
Usually this is not a problem, but if you transition to a new activity/fragment, you can't rely on FirebaseAuth.getInstance().getCurrentUser()
being populated yet.
You have two options here:
- Put the value of
authResult.getUser()
in shared preferences, and use that in the next activity/fragment as the current user. Attaching an auth state listener with
FirebaseAuth.getInstance().addAuthStateListener(listener)
in the next activity/fragment, and wait for the user to be initialized there.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"); } // ... } });
Answered By - Frank van Puffelen
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.