Android Architecture Components with Firebase

Hello everyone,

I have noticed that I haven’t written a blog for a long time and I wanted to break it with good content :)

One of the improvements that came to Android in May 2017 is Architecture Components, until the last Google I/O, the Android system didn’t recommend any specific Architecture for application development. The Android team has created a solution to this issue and announced Android Architecture Components.

In this blog, I would like to talk about using Firebase with Architecture Components.

In simple applications, it is possible to manage listeners with onStart()and onStop() methods but as the application grows, it becomes complicated. Data won’t survive configuration changes, the same is true for testability and readability.

We will get help from two components to solve these problems ; LiveData and ViewModel, i suggest you examine LiveData and ViewModel before you start.

Let’s go through an application where we can easily save and read data the picture.

1. LiveData

LiveData is an observable data holder class.

public class FirebaseQueryLiveData extends LiveData<DataSnapshot> {//...private final Runnable removeListener = new Runnable() {
@Override
public void run() {
query.removeEventListener(valueListener);
listenerRemovePending = false;
}
};

@Override
protected void onActive() {
if (listenerRemovePending) {
handler.removeCallbacks(removeListener);
}
else {
query.addValueEventListener(valueListener);
}
listenerRemovePending = false;
}

@Override
protected void onInactive() {
handler.postDelayed(removeListener, 2000);
listenerRemovePending = true;
}
private class mValueEventListener implements ValueEventListener{

@Override
public void onDataChange(DataSnapshot dataSnapshot) {
setValue(dataSnapshot);
}

@Override
public void onCancelled(DatabaseError databaseError) {

}
}
//...
}

LiveData only updates app component observers that are in an active lifecycle state. For example, the onActive() method adds a database listener and removes it with the onInactive() method. It is actively detected in Started or Resumed states. I used a handler here. The Activity configuration change does not cause the listener to be removed and re-added. We can use this class for every Firebase query.

2. ViewModel

Another component ViewModel, is a class designed to hold data about the UI and maintain integrity during configuration changes (such as device rotations).

public class MessagesListViewModel extends ViewModel {    private List<Entity> mList = new ArrayList<>();    private static final DatabaseReference dataRef =
FirebaseDatabase.getInstance().getReference().child("messages");

@NonNull
public LiveData<List<Entity>> getMessageListLiveData(){
FirebaseQueryLiveData mLiveData = new FirebaseQueryLiveData(dataRef);

LiveData<List<Entity>> mMessageLiveData =
Transformations.map(mLiveData, new Deserializer());
return mMessageLiveData;
}

I created a view model, extend the ViewModel class. The utility class Transformations provides a static method map() that returns a new LiveData object given a source LiveData object and a Function implementation. This new LiveData applies the Function to every object emitted by the source, then turns around and emits the output of the Function.

//...private class Deserializer implements Function<DataSnapshot, List<Entity>>{

@Override
public List<Entity> apply(DataSnapshot dataSnapshot) {
mList.clear();
for(DataSnapshot snap : dataSnapshot.getChildren()){
Entity msg = snap.getValue(Entity.class);
mList.add(msg);
}
return mList;
}
}
//...

Deserialize DataSnapshot into a List<Entity>

Now, let’s see how you use this from view.

You may call ViewProviders.of(getActivity()).get(MessagesListViewModel.class). This factory method will return a new instance of the ViewModel or get the retained one, as appropriate.

//MessagesListFragment.classmModel = ViewModelProviders.of(getActivity()).get(MessagesListViewModel.class);//...LiveData<List<Entity>> liveData = mModel.getMessageListLiveData();

liveData.observe(getActivity(), (List<Entity> mEntities) -> {
mMessageAdapter.setMessageList(mEntities);
});
//....

With this structure, we can access and observe the ViewModel class.

For another example, I created two MutableLiveData in the MessagesListViewModel.

private final MutableLiveData<Boolean> pictureUploadIsSuccessful = new MutableLiveData<>();
private final MutableLiveData<Boolean> messageUploadIsSuccessful = new MutableLiveData<>();


public MutableLiveData<Boolean> getPictureUploadIsSuccessful(){
return pictureUploadIsSuccessful;
}

public MutableLiveData<Boolean> getMessageUploadIsSuccessful(){
return messageUploadIsSuccessful;
}

When the data upload is completed, I set it to true.

uploadTask.addOnSuccessListener(o -> messageUploadIsSuccessful.setValue(true));uploadTask.addOnSuccessListener(o -> pictureUploadIsSuccessful.setValue(true));

If I go to back to the view, I can update the UI by observing the previous method in a similar way.

mViewModel = ViewModelProviders.of(getActivity()).get(MessagesListViewModel.class);

mViewModel.getPictureUploadIsSuccessful().observe(this, isSuccess -> {
if(isSuccess){

if(!mViewModel.getPhotoUrl().isEmpty()){
Glide.with(mBinding.photoView.getContext())
.load(mViewModel.getPhotoUrl())
.into(mBinding.photoView);

toast.makeText(getContext(),"Picture Upload successful",Toast.LENGTH_SHORT).show();
}
}
else{
toast.makeText(getContext(),"Could not fetch the picture!",Toast.LENGTH_LONG).show();
}
});

mViewModel.getMessageUploadIsSuccessful().observe(this, isSuccessful -> {
if(isSuccessful && !mViewModel.getPhotoUrl().isEmpty()){
toast.makeText(getContext(),"Message Upload successful",Toast.LENGTH_SHORT).show();
}
});

You can find the project here.

Thanks for reading.

Don’t forget the clap if you like this blog :)

Thanks to Alp çelik

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store