Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase Android onAuthStateChanged() fire twice after signInWithEmailAndPassword()

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 inonStop()` 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;
    }
}
like image 811
Terrence Avatar asked Jun 07 '16 08:06

Terrence


2 Answers

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

like image 130
user3597165 Avatar answered Oct 25 '22 18:10

user3597165


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:

  • Registration call: skip the first call with a flag.
  • User signed in: user from parameter is != null.
  • User signed out: user from parameter is == null.
  • Current user changes: user from parameter is != null and last user id is != user id from parameter
  • User token refresh: user from parameter is != null and last user id is == user id from parameter

This listener is a mess and very bugprone. Firebase team should look into it.

like image 23
Fabricio Avatar answered Oct 25 '22 19:10

Fabricio