In the previous posts (part 1 and part 2), we looked into the new Architecture Components that were announced at Google I/O 2017. The Android Architecture components are a welcome addition to the Android Platform. Previously, it was difficult to architect Android applications as there were no official guidelines. The new Architecture Components help solve some of the more difficult problems that you may have faced when building your Android applications in the past.
In this blog post, we will examine the new classes that were introduced to handle Lifecycle changes.
The Problem
Let’s imagine a situation where we have a video player in our app. In this same activity we might also have other operations such as collecting usage data and listeners for external events (for example – network changes). Typically, you may end up with code that looks like following example:
public class VideoActivity extends Activity { @Override protected void onStart(){ videoPlayer.start(); analytics.start(); //.. } @Override protected void onStop(){ videoPlayer.stop(); analytics.stop(); //.. } }
Now this may not look like a problem at first but if you are dealing with a very complicated activity or fragment, your activity will probably become quite cluttered by managing components in the different lifecycle methods.
You will end up spending a lot of time ensuring that the correct method is called in the correct lifecycle event. This also means that developers who design different components need to expose the relevant lifecycle methods that need to be called in the corresponding stage of the activity’s lifecycle. They also need to ensure that their documentation correctly covers where this code should be placed and why it exists.
Anyone that has worked with a video player before will know that there are many intricacies to be aware of and it can be confusing to know where certain methods should be called.
You may also need to check that certain methods are only called when the activity is in a certain state. For instance, if you had a listener that had to be registered in the onStart method but you first needed to do some asynchronous operation, it could happen that the task completes once the activity is already paused or stopped. Typical solutions that I’ve seen include keeping a few variables to keep track of activity state and then checking that variable or using RxJava subscriptions and unsubscribing to them in the relevant lifecycle methods.
The Solution – Use a LifecycleObserver
Using the new Android Architecture components libraries, there is now an easier way to work with lifecycles! Instead of exposing methods and calling them individually in each lifecycle method, you can now annotate your custom methods in the component with the lifecycle event that will trigger it.
The new Lifecycle class exposes two enums, an Event enum and a State enum. Events are emitted for every lifecycle change and the state can be queried at any point on a Lifecycle object. This diagram from the official Android documentation explains these enums well:
When implementing your own LifecycleObserver, you can then annotate your methods inside the custom observer. These methods will be executed on lifecycle state changes. A good example of a LifecycleObserver is the LiveData class. Let’s look at how we can implement the LifecycleObserver interface.
Create your own Lifecycle Aware Component using the LifecycleObserver interface
First add the Google maven repository to your project level build.gradle file:
allprojects { repositories { jcenter() google() } }
Then make sure you are importing the lifecycle library into your app’s build.gradle file:
compile "android.arch.lifecycle:runtime:1.0.0-beta2" compile "android.arch.lifecycle:extensions:1.0.0-beta2" annotationProcessor "android.arch.lifecycle:compiler:1.0.0-beta2"
Now we can create a VideoPlayerComponent class that implements the LifecycleObserver interface. Each method is then annotated with the relevant @OnLifecycleEvent annotation.
public class VideoPlayerComponent implements LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) void onCreate() { //.. } @OnLifecycleEvent(Lifecycle.Event.ON_START) void onStart() { if (Util.SDK_INT > 23) { initializePlayer(); } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) void onStop() { if (Util.SDK_INT > 23) { releasePlayer(); } } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) void onPause() { if (Util.SDK_INT <= 23) { releasePlayer(); } } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) void onResume() { if ((Util.SDK_INT <= 23)) { initializePlayer(); } } //.. }
Now in our activity, all we need to do is add this custom LifecycleObserver to observe the current lifecycle. The VideoPlayerComponent will then run independently without having to manually call the different methods in the onStart, onStop etc.
public class VideoPlayerActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //.. getLifecycle().addObserver(new VideoPlayerComponent(//..)); } }
This is a much better solution to structuring our components that need to be aware of the Lifecycle of the enclosing activity or fragment.
The full code example can be found here. (Please note that this video player example doesn’t take care of all different kinds of media types and DRM management, it is merely an example to show you how you can clean up your activities and use the Lifecycle capabilities of the library).
Have something to add? Let me know on Twitter @riggaroo.
Still curious about how the Architecture components work? Watch this live hangout where we covered all the components in a nutshell.