Android – Working with live data and custom life cycle owners.

I came across Android architecture components a few days ago and one of the things i found interesting was live data. So i decided to test the functionality and see if it is something that i can use in my projects . Before i continue with this post , i would like to remind you that these new libraries are in preview , here is what was written on the android website.

Architecture Components are still in alpha. Expect breaking changes before the 1.0 release.

So , i am not sure if the things that i am working with today will look and act the same in the future. What i am sure of however is that the concepts will remain more or less the same.

The reason why i found live data interesting was because while working with firebase , i had to manually detach my value event listeners whenever i was finished with an activity . This was a very exhausting task since in every activity , i had at least four to five value event listeners.

When i went through the documentation of live data , i observed that it can prove useful for detaching these listeners.

Here is how the app will look. Pretty unremarkable.

Screenshot_2017-05-27-16-25-38

Before i go through the example that i was working on , let me give you a brief overview of what live data does and what its advantages are.

Suppose a source emits events and a listener reacts to these events , we would ideally want the listener to stop reacting when we are finished with an activity, fragment or say a custom life cycle owner ( we will implement our own life cycle owner in this post).

What live data does is , keep track of the life cycle events of the owner and remove observers when the life cycle state is stopped or destroyed.

In order to set up architecture components , visit this adding components to your project page.

 

This example , will have a spinner with three options , dog , cat and rabbit.

The image will change based on the animal selected. Therefore the PictureSetter will act as the observer.

AnimalType class will act as the live data because it continously emits the latest animal selected.


public class AnimalType extends LiveData<String> implements AnimalSelectionListener{

@Override
protected void onActive() {
Log.i("Inside AnimalType","Active");
}

@Override
protected void onInactive() {
Log.i("Inside AnimalType","InActive");
}

@Override
public void onAnimalSelected(String animalName) {
setValue(animalName);
}
}

 

The onActive and onActive will be called when there are active observers and zero active observers respectively.

 

Here is the picture setter class.


public class PictureSetter implements Observer<String>{
ImageView animalImage;
Context context;

public PictureSetter(ImageView animalImage,Context context) {
this.animalImage = animalImage;
this.context = context;
}

@Override
public void onChanged(@Nullable String imageName) {
Log.i("PictureSetter","Setting image "+imageName);
if(imageName.equalsIgnoreCase("cat")){
setImage(R.drawable.cat);
}
else if(imageName.equalsIgnoreCase("dog")){
setImage(R.drawable.dog);
}
else
{
setImage(R.drawable.rabbit);
}
}

private void setImage(int drawableId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
animalImage.setBackground(context.getDrawable(drawableId));
}
else
{
animalImage.setBackground(context.getResources().getDrawable(drawableId));
}
}
}

 

The onChanged method will be fired whenever the data changes , so we set the image based on the option selected.

Here is how the Animal Type data changes , the AnimalType class implements the animalSelectionListener interface , so whenever we choose a new a animal , it receives new data.


animalSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
if(animalSelectionListener != null){
animalSelectionListener.onAnimalSelected(animals[i]);
}
}

@Override
public void onNothingSelected(AdapterView<?> adapterView) {

}
});

&nbsp;

public interface AnimalSelectionListener {
void onAnimalSelected(String animalName);
}

We are finished with setting up a data source and an observer , we now have to implement a custom life cycle owner and control its life cycle to make sure that live data works as expected.


private class CustomLifeCycleOwner implements LifecycleOwner{

private LifecycleRegistry mLifecycleRegistry;

public CustomLifeCycleOwner() {
mLifecycleRegistry = new LifecycleRegistry(this);
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
}

public void stopListening(){
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
}

public void startListening(){
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
}

@Override
public Lifecycle getLifecycle() {
Log.i("CustomLifeCycleOwner","Returning registry!!");
return mLifecycleRegistry;
}
}

 

Here is what the doc says about life cycle owners

Defines an object that has an Android Lifecycle. Fragment and FragmentActivity classes implement LifecycleOwner interface which has the getLifecycle method to access the Lifecycle. You can also implement LifecycleOwner in your own classes.

The LifecycleRegistry class extends the LifeCycle class and is responsible for emitting life cycle events to LifeCycleObserver classes.

By calling the startListening and stopListening methods we control the the life cycle of the our custom life cycle owner. In our example , we will have a button which will do this controlling.

This is how we bring all of the code together.


customLifeCycleOwner = new CustomLifeCycleOwner();

animalType.observe(customLifeCycleOwner,pictureSetter);

 

When we call the observe method on animalType we pass two parameters , one is the life cycle owner and the other is the observer.

We control the life cycle like this


private void startPictureSetter(){
changeStatusButton.setText("STOP CHANGING PICTURES");
customLifeCycleOwner.startListening();
}

private void stopPictureSetter(){
changeStatusButton.setText("START CHANGING PICTURES");
customLifeCycleOwner.stopListening();
}

Some of the benifits of using live data as per the Android website are

  • No memory leaks: Since the Observers are bound to their own Lifecycle objects, they are automatically cleaned when their Lifecycle is destroyed.
  • No crashes due to stopped activities: If the Observer’s Lifecycle is inactive (like an activity in the back stack), they won’t receive change events.
  • Always up to date data: If a Lifecycle starts again (like an activity going back to started state from the back stack) it receives the latest location data (if it didn’t already).
  • Proper configuration change: If an activity or fragment is re-created due to a configuration change (like device rotation), it instantly receives the last available Location data.
  • Sharing Resources: Now we can keep a single instance of MyLocationListener, connect to the system service just once, and properly support all observers in the app.
  • No more manual lifecycle handling: As you might notice, our fragment just observes the data when it wants to, does not worry about being stopped or start observing after being stopped. LiveData automatically manages all of this since the fragment provided its Lifecycle while observing.

 

Thats it for this post . If you anything to say , please add a comment below , thanks for reading!.

 

 

 

 

 

 

 

One thought on “Android – Working with live data and custom life cycle owners.

  1. what you fail to explain / show is the very 1st reason that got you interested into LiveData:

    “The reason why i found live data interesting was because while working with firebase , i had to manually detach my value event listeners whenever i was finished with an activity . This was a very exhausting task since in every activity , i had at least four to five value event listeners.

    When i went through the documentation of live data , i observed that it can prove useful for detaching these listeners.”

    I would guess you have to manually trigger stopPictureSetter() in order to tell to the LifecycleOwner to stop listening.

    Like

Leave a comment