Codementor Events

Realtime Applications using Android Architecture Components with Kotlin

Published Dec 08, 2017Last updated Jun 06, 2018
Realtime Applications using Android Architecture Components with Kotlin

Android Architecture Components version 1.0.0 has now been released. This means their APIs are now stable and you should be more comfortable with adopting it in your projects.

What are Android Architecture Components?

Android Architecture components (AAC) are a set of Android libraries that help you structure your application in a way that is robust, testable, and maintainable.

​​In this post, we are going to discuss how to structure an Android application’s code for real-time updates using Android Architecture Components. We will be talking specifically about using ViewModel and LiveData to build an Android application that will be updating in real-time.

​​Let’s discuss Architecture

AAC promotes the Model-View-ViewModel (MVVM) architectural pattern in Android applications adopting it. The MVVM model can be illustrated as shown in the image below:
​​  User-uploaded image: pusher-kotlin-realtime-app-architecture-component-1.jpg

  • ​​The View: The View is usually a dummy component. Its sole purpose is to display UI items to the application's user. It does this by observing changes to its underlying ViewModel and displaying those changes to the screen.

  • ​​The ViewModel: The ViewModel represents the current state of a View and any update to the ViewModel is propagated to the View for display. The ViewModel observes changes to data from relevant data sources in the Model layer and these changes get propagated to the View for display.

  • ​​The Model: The Model layer (also called the Data layer) represents the sources of data that are required to drive the application's business logic. Changes to data are exposed via an observable interface so that other application components can observe and react accordingly.

​​Now, AAC provides components that make implementing each of the MVVM layers easy. Let's take a quick look at each of the AAC components and how they help in an MVVM Architecture.

The Android Architecture Components

​​There are four major Architecture Components. Below are brief descriptions of each of these components and how they help in implementing an MVVM architecture.

  1. ​​Lifecycle: Most of the app components defined in the Android Framework have lifecycles attached to them. Lifecycle is a class that holds the information about the lifecycle state of a component (like an activity or a fragment) and allows other objects to observe this state. This makes it easy to create objects that react to the state of a component. For example, a GPS location service can observe the state of an activity and stop location updates when the activity is in pause state.

  2. ​​ViewModel: The ViewModel class is designed to store and manage UI-related data so that the data survives configuration changes, such as screen rotations. This feature makes the class perfect for the ViewModel layer of an application.

  3. ​​LiveData: LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state. This feature makes it quite useful for data communication between the various MVVM layers.

  4. ​​Room: Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. Room becomes quite useful in the data layer for persistence and also providing real-time updates when data changes.

​​Android Architecture Components for Realtime Applications

​​Now, let's see how we can apply AAC to building a real-time application.

​​A key component to developing a real-time application is to have a real-time data layer that is capable of emitting new data that will be observed by the upper layer. 

​​To make this easier, let us walk through a sample application that provides real-time features. The application will be simple. It will have a RecyclerView showing a list of posts that will be updated in real-time as new data is pushed to the application. Below is a video of how the application works:
​​  User-uploaded image: pusher-kotlin-realtime-app-architecture-component-2.gif

​​To follow along, head over to this repository and clone the project. Import the application into Android Studio and let it build. We will be stepping through this codebase to learn more about the AAC.

​​Before we go through the code, below is an overview of how the codebase is structured:

​​  User-uploaded image: pusher-kotlin-realtime-app-architecture-component-3.jpgRealtime Application Architecture

​​Let's go through the code from the top layer (the view layer) down to the bottom layer (data layer) and see how the real-time updates are implemented.

​​ 1. PostsActivity (The View Layer)

​​The PostsActivity.kt file can be found in the com.pusher.realtimearchitecture.presentation package. Its main purpose is to observe changes in the list of posts in the ViewModel and display those changes on a RecyclerView list view. Below is an excerpt of its code:

...
class PostsActivity : AppCompatActivity() {

    private val postsListAdapter by lazy {
        PostsListAdapter(this)
    }
    
    private val postsViewModel: PostsViewModel by lazy {
        val viewModelFactory = ViewModelFactory(this)
        val viewModelProvider = ViewModelProviders.of(this, viewModelFactory)
        viewModelProvider.get(PostsViewModel::class.java)
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_feed)

       postsListView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
        postsListView.adapter = postsListAdapter

        postsViewModel.postItems.observe(this, Observer { items ->
            items?.also { postsListAdapter.items = it }
        })
    }
}

In the onCreate() method, we set the views layout to activity_feed (which contains only a RecyclerView with ID postsListView) and then we set its layout manager and list adapter.

Next, we observe changes on the postItems exposed by the postsViewModel. These changes are then passed to the list adapter to update the postsListView items.

postsViewModel is a lazy initialized object of the PostsViewModel class. An instance of the PostsViewModel is obtained using the AAC ViewModelProvider.get() method.

To get the ViewModelProvider attached to an activity, we used the ViewModelProviders.of() static factory method by passing in the activity and a ViewModelFactory object as arguments.

The purpose of the ViewModelFactory is to assist the ViewModelsProviders.of() static method in constructing the required ViewModel classes (in this case PostsViewModel).

We will explore the ViewModelFactory class in more detail later.

Next, let's take a look at the PostsViewModel class.

2. PostsViewModel (The ViewModel Layer)

The PostsViewModel.kt file can also be found in the com.pusher.realtimearchitecture.presentation package:

package com.pusher.realtimearchitecture.presentation
...
class PostsViewModel(fetchPosts: FetchPosts): ViewModel() {

    var postItems = MediatorLiveData<PostItems>()

    init {
        postItems.addSource(fetchPosts.execute(), {
            postItems.setValue(it)
        })
    }
}

The PostsViewModel has a postItems property which is a MediatorLiveData of PostItems.

A MediatorLiveData is a subclass of LiveData that can observe other LiveData objects and react to changes in their streams. This makes it easy to pipe data from an Observable Source to a Destination.

Remember that postItems is being observed inside PostsActivity, so PostViewModel fetches posts from the data source using the FetchPosts object and pipes the returned data to be published to all postItems subscribers.

Note: The PostItems type is actually a Kotlin alias defined in the com.pusher.realtimearchitecture.domain.dto.PostItem class as shown below:

data class PostItem(val title: String, val description: String)
typealias PostItems = List<PostItem>

FetchPosts encapsulates the logic for fetching posts from the data source. It is usually good practice to have classes that perform a particular use case or functionality in your application.

This makes it easy to test and re-use the logic anywhere in your application. Below is a code excerpt of the FetchPosts class:

package com.pusher.realtimearchitecture.domain
...

class FetchPosts(val postsRepo: PostsRepository) {
    fun execute(): LiveData<PostItems> {
        return Transformations.map(postsRepo.fetchPosts(), this::mapModelsToItems)
    }
    
    private fun mapModelsToItems(models: PostModels) 
        = models.map { PostItem(it.title, it.description) }
}

FetchPosts takes a PostsRepository object as an argument and calls fetchPosts() on the repository. The returned Post model is then converted to PostItem objects to be used by the View layer.

Now, let us look at the PostsRepository, which is the final layer of our realtime application.

3. PostsRepository (The Data Layer)

The PostsRepository represents an interface for managing the various post data sources that drive the application. You would find the interface definition for PostsRepository in the com.pusher.realtimearchitecture.data package. Below is an excerpt of the code:

interface PostsRepository {
    fun fetchPosts(): LiveData<PostModels>
}

Note: PostModels is also a Kotlin alias defined in com.pusher.realtimearchitecture.data.dto.PostModel:

data class PostModel(val title: String, val description: String)
typealias PostModel = List<PostModel>

Now, since our Android application is going to have a real-time data source, we created a RealtimePostRepository, which is an implementation of the PostRepository:

package com.pusher.realtimearchitecture.data
...
class RealtimePostsRepository(private val realtimeDataSource: PusherPostDataSource)
                                                        : PostsRepository {
    override fun fetchPosts() = realtimeDataSource.fetchPosts()
  }

For the purpose of this tutorial, we will be using Pusher as our real-time data source. Head over to pusher.com and create an application. After successfully creating the Pusher application, some API keys will be generated. You should copy these keys, as they will be used later in this tutorial.

So RealtimePostsRepository takes a PusherPostDataSource object as its constructor argument and then delegates calls from RealtimePostsRepository.fetchPosts() to PusherPostDataSource.fetchPosts().

Now, let us walk through how PusherPostDataSource is implemented. This class can be found in the com.pusher.realtimearchitecture.data.pusher package:

package com.pusher.realtimearchitecture.data.pusher

class PusherPostDataSource(private val lifecycleOwner: LifecycleOwner)
    : LifecycleObserver {
  
    init {
      lifecycleOwner.lifecycle.addObserver(this)
    }
    ...

PusherPostDataSource implements the LifecycleObserver interface, which is important because we want this data source to be aware of the Lifecycle of Android Components that would be observing the data it would be emitting. Basically, PusherPostDataSource should only be emitting data when the lifecycleOwner is in an active/visible state.

class PusherPostDataSource(private val lifecycleOwner: LifecycleOwner)
    : LifecycleObserver {
    ...
    companion object {
        private const val PUSHER_API_KEY = "PUSHER_API_KEY_HERE"
        private const val PUSHER_APP_CLUSTER = "PUSHER_CLUSTER_HERE"
        private const val PUSHER_POST_CHANNEL = "posts-channel"
        private const val PUSHER_NEW_POST_EVENT = "new-posts"
    }

    private val pusher = Pusher(PUSHER_API_KEY, PusherOptions().setCluster(PUSHER_APP_CLUSTER))

    private var postModels: PostModels = mutableListOf()
    private val postsStream = MutableLiveData<PostModels>()
    ...

Next, we define some constants that will be required by Pusher to broadcast real-time events. Afterwards, a pusher object is created using the Pusher API key and configuration options.

Next, we initialize postModels, a list of PostModel to store each of the posts that will be received from Pusher’s broadcasts.

We also initialized a MutableLiveData object of PostModels. This will be the data stream that will be exposed to the other layers in the applications.

class PusherPostDataSource(private val lifecycleOwner: LifecycleOwner)
    : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun onStart() {
        val channel = pusher.subscribe(PUSHER_POST_CHANNEL)
        channel.bind(PUSHER_NEW_POST_EVENT) { _, _, data ->
            val post = Gson().fromJson(data, PusherPostEntity::class.java)
            postModels = postModels.plus(PostModel(post.title, post.description))
            postsStream.postValue(postModels)
        }
    }

    fun fetchPosts() = postsStream
    ...
}

Next, we define the onStart() method, which would be run when the lifecycleOwner is entering the STARTED state.

The @OnLifecycleEvent annotation is used to specify when in the Lifecycle of an Android Component a function should be run. For example, in the PusherPostDataSource, the annotation on the onStart() method is used to specify that the method should be run when the component is entering the CREATED state (ie. when the ON_CREATE event is received).

The onStart() method subscribes to the Pusher posts channel and binds to the new-posts events. When a new post is received, the JSON object string representing the PusherPostEntity is deserialized and appended to the lists of postModels. The updated postModels are then broadcasted through the postStream object using its postValue() methods.

The fetchPosts() method returns the postsStream object so it can be observed by its calling component.

class PusherPostDataSource(private val lifecycleOwner: LifecycleOwner)
    : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun onResume() = pusher.connect()
    
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun onPause() = pusher.disconnect()
    ...
}

And finally, we connect the pusher object when the lifecycleOwner object is entering the RESUMED state and disconnect it when the ON_PAUSE event is emitted.

And there you have it, all the layers required to build a realtime MVVM Android application using Android Architecture Components.

The last thing we will talk about is how all this different layers are connected to each other. All of these different layers are constructed and connected to each other in the ViewModelFactory class.

The ViewModelFactory (The Glue)

Remember the ViewModelFactory class that was used in the PostsActivity when creating an instance of a ViewModelProvider? Its major purpose is to configure the PostsViewModel. Its code can be found in the com.pusher.realtimearchitecture.presentation package:

class ViewModelFactory(private val lifecycleOwner: LifecycleOwner)
    : ViewModelProvider.Factory {

    override fun <T: ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(PostsViewModel::class.java)) {
            val realtimeDataSource = PusherPostDataSource(lifecycleOwner)
            val realtimeFeedRepository = RealtimePostsRepository(realtimeDataSource)
            val fetchFeedUseCase = FetchPosts(realtimeFeedRepository)
            return PostsViewModel(fetchFeedUseCase) as T
        }
        throw IllegalArgumentException("Unknown View Model class name")
    }
}

ViewModelFactory takes a LifecycleOwner object as an argument and implements the ViewProvider.Factory interface. It needs to override the create() method of the ViewProvider.Factory interface, which will be called by the ViewModelProvider when an instance of ViewModel is to be instantiated.

In this tutorial, since we only have one ViewModel class of interest (the PostsViewModel class), we check if the modelClass passed to the create() method is a PostsViewModel type. We then construct the PostViewModel dependencies and use those dependencies to create the PostsViewModel object that is then returned from the create() method.

Now you have a fully functional real-time Android application using Android Architecture Components.

Testing the real-time App

To test the real-time updates in the Android application, there are two possible ways:

  1. You can head over to the Debug Console of your Pusher Application and then send a new-posts event to the posts-channel channel with the following data:

    {
    "title": "This is another post",
    "description": "Lorem ipsum dolor sit amet. So many things here"
    }

  2. The second way to test this is to build a Server that would be broadcasting Pusher events. See here for details of how to implement this in your favorite programming language.

Final Thoughts

We have seen how Android Architecture Components can be used to neatly structure our application to be robust and maintainable.

One thing to note is that it is not compulsory to use all of the libraries provided in the Architecture Components together. For example, you can decide to use RxJava’s observable streams instead of LiveData or another database library instead of Room.

This means that you are not locked down to the limitation of AAC libraries, for example RxJava provides better multithreading features out of the box than LiveData does. It also means you can migrate your existing project one step at a time by substituting Architecture Components into each layer.

This article just touches a little on what can be done with Android Architecture Components, but you certainly have enough knowledge by now to understand the logic behind the architecture and the roles of the different Architecture Components.

To further increase your knowledge of the Components, you can visit the AAC website here to read their documentation and try out the various codelabs.
The full code for the sample application in this article can be found here.

So what do you think? Let me know in the comments below what your thoughts are on the Android Architecture Components.


This post was originally posted as a Guest Post on Pusher's Blog here.

Discover and read more posts from Perfect Makanju
get started
post commentsBe the first to share your opinion
Show more replies