When I use signInWithEmailAndPassword()
to login the onAuthStateChanged()
always fire twice.
I'm very sure that the listening is only added once to firebaseAuth, and I have the code in
onStop()` to remove the listener after that.
Anyone know how to solve this?
My code:
public class SignInActivity extends BaseActivity implements
View.OnClickListener,
GoogleApiClient.OnConnectionFailedListener{
private static final String PREF_KEY_USER_EMAIL = "User_Email";
private static final int RC_SIGN_IN = 1111;
private FirebaseAuth firebaseAuth;
private FirebaseAuth.AuthStateListener authStateListener;
private DatabaseReference firebaseDbReference;
private TextView fieldEmail;
private TextView fieldPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_in);
getSupportActionBar().hide();
firebaseAuth = FirebaseAuth.getInstance();
firebaseDbReference = FirebaseDatabase.getInstance().getReference();
fieldEmail = (TextView) findViewById(R.id.field_email);
fieldPassword = (TextView) findViewById(R.id.field_password);
String userSavedEmail = getPreferences(MODE_PRIVATE).getString(PREF_KEY_USER_EMAIL, "");
if(userSavedEmail != null) {
fieldEmail.setText(userSavedEmail);
fieldPassword.requestFocus();
}
TextView linkForgotPassword;
Button buttonLogin;
linkForgotPassword = (TextView) findViewById(R.id.link_forgotPassword);
buttonLogin = (Button) findViewById(R.id.button_Login);
buttonSignUp = (Button) findViewById(R.id.button_signUp);
if (linkForgotPassword != null) {
linkForgotPassword.setOnClickListener(this);
}
if (buttonLogin != null) {
buttonLogin.setOnClickListener(this);
}
if (buttonSignUp != null) {
buttonSignUp.setOnClickListener(this);
}
authStateListener = new FirebaseAuth.AuthStateListener() {
@Override
public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
if(firebaseAuth.getCurrentUser() != null) {
onAuthSuccess(firebaseAuth.getCurrentUser());
}
}
};
}
@Override
protected void onStart() {
super.onStart();
firebaseAuth.addAuthStateListener(authStateListener);
}
@Override
protected void onStop() {
super.onStop();
if (authStateListener != null) {
firebaseAuth.removeAuthStateListener(authStateListener);
}
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.link_forgotPassword:
forgotPassword();
break;
case R.id.button_Login:
emailLogin();
break;
case R.id.button_signUp:
emailSignUp();
break;
}
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Toast.makeText(this, "Google Play Services error.", Toast.LENGTH_SHORT).show();
}
private void forgotPassword(){
FirebaseAuth auth = FirebaseAuth.getInstance();
String emailAddress = fieldEmail.getText().toString();
if(TextUtils.isEmpty(emailAddress)){
Toast.makeText(SignInActivity.this, R.string.msg_EnterEmail,
Toast.LENGTH_SHORT).show();
}
else {
showProgressDialog();
auth.sendPasswordResetEmail(emailAddress)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
hideProgressDialog();
if (task.isSuccessful()) {
Toast.makeText(SignInActivity.this, R.string.msg_ResetPasswordEmailSent,
Toast.LENGTH_LONG).show();
}
}
});
}
}
private void emailLogin(){
if (!validateForm()) {
return;
}
showProgressDialog();
String email = fieldEmail.getText().toString();
String password = fieldPassword.getText().toString();
firebaseAuth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
hideProgressDialog();
if (!task.isSuccessful()) {
Toast.makeText(SignInActivity.this, R.string.msg_EmailLoginFailed,
Toast.LENGTH_SHORT).show();
}
else {
// Save the email
getPreferences(MODE_PRIVATE).edit()
.putString(PREF_KEY_USER_EMAIL, fieldEmail.getText().toString())
.apply();
}
}
});
}
private void emailSignUp(){
if (!validateForm()) {
return;
}
showProgressDialog();
String email = fieldEmail.getText().toString();
String password = fieldPassword.getText().toString();
firebaseAuth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
hideProgressDialog();
if (task.isSuccessful()) {
FirebaseUser user = task.getResult().getUser();
String displayName = displayNameFromEmail(user.getEmail());
// Update profile display name.
UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
.setDisplayName("Jane Q. User")
.build();
user.updateProfile(profileUpdates);
} else {
Toast.makeText(SignInActivity.this, R.string.msg_EmailSignUpFailed,
Toast.LENGTH_SHORT).show();
}
}
});
}
private void onAuthSuccess(FirebaseUser user) {
// Write new user
writeNewUser(user.getUid(),
user.getDisplayName(),
user.getEmail(),
user.getPhotoUrl());
// Go to MainActivity
startActivity(new Intent(this.getApplicationContext(), MainActivity.class));
finish();
}
private void writeNewUser(String userId, String displayName, String email, android.net.Uri photoUrl) {
User user = new User(displayName, email);
if(photoUrl != null){
user.setPhotoUrl(photoUrl.toString());
}
firebaseDbReference.child("users").child(userId).setValue(user);
}
private String displayNameFromEmail(String email) {
if (email.contains("@")) {
return email.split("@")[0];
} else {
return email;
}
}
private boolean validateForm() {
boolean result = true;
if (TextUtils.isEmpty(fieldEmail.getText().toString())) {
fieldEmail.setError("Required");
result = false;
} else {
fieldEmail.setError(null);
}
if (TextUtils.isEmpty(fieldPassword.getText().toString())) {
fieldPassword.setError("Required");
result = false;
} else {
fieldPassword.setError(null);
}
return result;
}
}
It does fire twice and I think this is a bug that Firebase guys should fix (looking at you Frank hehehe ). The only thing I can think you can do right now is to add a flag like this.
private boolean flag = true;
...
authStateListener = new FirebaseAuth.AuthStateListener() {
@Override
public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
if(firebaseAuth.getCurrentUser() != null && flag) {
onAuthSuccess(firebaseAuth.getCurrentUser());
flag=false;
}
}
};
Far from ideal, but will work for now. Code still fires twice, we accept the first one and deny the second one with our flag, this way if Firebase guys fix it and suddenly the listener runs once, our code still works. Maybe it is intended for the listener to run twice, hopefully we will have some answers from Frank's cross-post
The double call is due a registration call. Not only that, onAuthStateChanged is going to be called many times in many different states, with no possibility of knowing which state it is.
Documentation says:
onAuthStateChanged(FirebaseAuth auth)
This method gets invoked in the UI thread on changes in the authentication state:
Right after the listener has been registered
When a user is signed in
When the current user is signed out
When the current user changes
When there is a change in the current user's token
Here some tips to discover the current state:
This listener is a mess and very bugprone. Firebase team should look into it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With