Do not copy and paste the Sign In and Sign Up display code into your own personal project. Recreate the functionality shown here with your own design and layout.
Now that we've created the necessary components, we're ready to add functionality allowing users to register for accounts. In this lesson we'll learn how to create Firebase user accounts within an Android application. In subsequent lessons we'll walk through updating user profile information, logging users in and out, and adding additional features and personalization to improve user experience.
After following along with the previous lesson, we should be greeted by the LoginActivity when the application launches. But what if a user doesn't have an account yet? Below the login form is a TextView
that reads "Don't have an account? Sign up here!":
Clicking this does not currently do anything. Let's make sure this link navigates to the CreateAccountActivity we created previously. We'll bind this view with ButterKnife, implement the View.OnClickListener
interface in the LoginActivity, and set a click listener and corresponding onClick()
function:
...
public class LoginActivity extends AppCompatActivity implements View.OnClickListener {
@Bind(R.id.registerTextView) TextView mRegisterTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.bind(this);
mRegisterTextView.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (view == mRegisterTextView) {
Intent intent = new Intent(LoginActivity.this, CreateAccountActivity.class);
startActivity(intent);
finish();
}
}
}
After an account is successfully authenticated, we no longer need the LoginActivity. In the code above, notice we include finish()
after creating our Intent
. This will destroy the LoginActivity as we depart for the CreateAccountActivity, saving us valuable resources. Check out the Android Documentation for more information on finish()
and other methods to halt unused or unnecessary activities.
Now we can launch our app, click "Don't have an account? Sign up here!", and see our registration form:
Now that our registration page is easily accessible, let's allow users to create their own accounts using our registration form.
First we'll implement click listeners, declare member variables, and bind elements from our activity_create_account.xml layout using ButterKnife. This should be review:
public class CreateAccountActivity extends AppCompatActivity implements View.OnClickListener {
@Bind(R.id.createUserButton) Button mCreateUserButton;
@Bind(R.id.nameEditText) EditText mNameEditText;
@Bind(R.id.emailEditText) EditText mEmailEditText;
@Bind(R.id.passwordEditText) EditText mPasswordEditText;
@Bind(R.id.confirmPasswordEditText) EditText mConfirmPasswordEditText;
@Bind(R.id.loginTextView) TextView mLoginTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_account);
ButterKnife.bind(this);
mLoginTextView.setOnClickListener(this);
mCreateUserButton.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (view == mLoginTextView) {
Intent intent = new Intent(CreateAccountActivity.this, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}
if (view == mCreateUserButton) {
createNewUser();
}
}
}
Here, we bind all elements of our registration form and add a click listener that will call createNewUser()
(which we will write momentarily) when the form is submitted. We also create a link back to the LoginActivity and add something called intent flags to manage our back stack of tasks.
Android manages tasks by placing activities started in succession in something called a stack. The last activity started is the first activity on the stack. When the system back button is selected, Android navigates to the last activity in the stack by default.
However, we don't actually want users navigating back to CreateAccountActivity after they've already created an account. They should no longer have any use for this form if they already have an account. To prevent this, we can explicitly remove these activities from our back stack altogether by setting flags on our intent.
Intent flags are essentially just extra pieces of information optionally added to an intent that determine how the specific intent is handled by Android. In the code above, we use the following flags:
FLAG_ACTIVITY_CLEAR_TASK will cause any existing task that would be associated with the activity to be cleared before the activity is started. This prevents the CreateAccountActivity from being unnecessarily accessed via the system back button.
FLAG_ACTIVITY_NEW_TASK will make the activity we are navigating to the start of a brand new task on this history stack.
For more information on tasks and the back stack, check out the Android Developer's guides article on Tasks and Back Stack.
Now that we can successfully submit the registration form, let's write the createNewUser()
method:
public class CreateAccountActivity extends AppCompatActivity implements View.OnClickListener {
public static final String TAG = CreateAccountActivity.class.getSimpleName();
...
private FirebaseAuth mAuth;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mAuth = FirebaseAuth.getInstance();
...
}
...
private void createNewUser() {
final String name = mNameEditText.getText().toString().trim();
final String email = mEmailEditText.getText().toString().trim();
String password = mPasswordEditText.getText().toString().trim();
String confirmPassword = mConfirmPasswordEditText.getText().toString().trim();
mAuth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "Authentication successful");
} else {
Toast.makeText(CreateAccountActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
}
}
});
}
}
First, we add a member variable to get the instance of the FirebaseAuth
object. This object is required in order to access the tools provided in the Firebase Authentication SDK. We then simply fetch the contents of our registration form (mNameEditText
, mEmailEditText
, mPasswordEditText
and mConfirmPasswordEditText
) and transform each value into a string.
Then, we call the built-in Firebase method createUserWithEmailAndPassword()
to create a new user account in Firebase, passing in the user's email and password. If the account can be created successfully, we log a success message to the logcat; otherwise we display a toast notifying the user something went wrong.
We should now be able to launch the application, navigate to the CreateAccountActivity, and submit our registration form. Note: Firebase Auth requires passwords be at least 6 characters long. Additionally, the email address must contain an @something.com
domain, otherwise it will not be recognized as a valid email address. Make sure to use a long enough password and correctly-formatted email address or you may encounter errors:
The app won't navigate anywhere after submitting this form (yet!), but if we visit the Auth tab in our Firebase dashboard and select Users , we can see a new Firebase user has been created:
Next, we need to inform our application when the user's account is successfully authenticated. To do this, we'll add an AuthStateListener
to respond to the change in the user's authentication state.
An AuthStateListener
simply listens for an account being successfully authenticated, or un-authenticated through Firebase. Firebase can also automatically authenticate user accounts upon registration. Therefore, our users can submit the registration form and if their account is created successfully they will be logged in automatically, and this listener will be triggered.
Inside of the onAuthStateChanged()
override, we will send the user to our MainActivity:
public class CreateAccountActivity extends AppCompatActivity implements View.OnClickListener {
...
private FirebaseAuth.AuthStateListener mAuthListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
createAuthStateListener();
}
...
private void createAuthStateListener() {
mAuthListener = new FirebaseAuth.AuthStateListener() {
@Override
public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
final FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
Intent intent = new Intent(CreateAccountActivity.this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}
}
};
}
}
First, we add the mAuthStateListener
member variable, setting it in our onCreate()
method by calling a new method, createAuthStateListener()
.
Inside of the new createAuthStateListener()
method, we create our new AuthStateListener
by setting our member variable to the FirebaseAuth.AuthStateListener
interface. This interface listens to changes in the current AuthState
. When there is a change (ie. a user becomes authenticated or signs out), this interface triggers the onAuthStateChanged()
method.
The onAuthStateChanged()
method returns FirebaseAuth
data. Using this data, we can create a FirebaseUser
by calling the getCurrentUser()
method. We double-check that this user is not null before traveling to the MainActivity.
Before we can test that our AuthStateListener
is working, we need to add it to our FirebaseAuth
object. We'll associate the two by calling addAuthStateListener()
in the onStart()
lifecycle method.
Additionally, we will also remove the listener before the activity is destroyed by calling the removeAuthStateListener()
method in the onStop()
lifecycle method:
...
@Override
public void onStart() {
super.onStart();
mAuth.addAuthStateListener(mAuthListener);
}
@Override
public void onStop() {
super.onStop();
if (mAuthListener != null) {
mAuth.removeAuthStateListener(mAuthListener);
}
}
...
The system calls the onStart()
method every time your activity becomes visible (whether it's being restarted or created for the first time).
Conversely, Android calls onStop()
when an activity is no longer visible. Once the activity is stopped, the system might destroy the instance if it needs to recover system memory. In extreme cases, the system might simply kill your app process without calling the activity's final onDestroy()
callback. This is why it is important to use onStop()
to release resources that might leak memory.
Note: If we forget to add the AuthStateListener
to our FirebaseAuth
object, our AuthStateListener
will not work!
Now, let's run our app to see our AuthStateListener
in action. If everything was configured properly, our app should bring the user to the MainActivity once their account is created
If we restart the app and navigate to CreateAccountActivity, you will notice that our app immediately brings us back to the MainActivity! When the AuthStateListener
is added in the onStart()
method, it checks to see if a user has already been authenticated. Because the user we created moments ago was logged in automatically, the AuthStateListener
brings them to the MainActivity.
In the next lesson we'll learn how to log the current user out so we can continue to build our CreateAccountActivity, and see our new features in action.
Stack: A collection of activities users interact with when performing a certain action with an application. The activities are arranged in a stack, ordered by when they were opened.
Intent flags: Additional information that can be optionally included with an intent that control how the specific intent is handled.
AuthStateListener: Part of an interface that listens to changes in the current AuthState
. When there is a change (ie. a user becomes authenticated or signs out), this interface triggers the onAuthStateChanged()
method automatically.
onStart()
method every time an activity becomes visible (whether being restarted or created for the first time). When an activity receives a call to the onStop()
method, it's no longer visible and should release almost all resources that aren't necessary while the activity is not in use.Check out the Android Documentation for more information on finish()
and other methods to halt activities.
For more information on tasks and the back stack, check out the Android Developer's guides article on Tasks and Back Stack.
For a list of all available intent flags, check out the documentaiton for the setFlags()
method here.