Android Architecture Components with Rx: Simple app

Konrad Szewczuk
AndroidPub
Published in
4 min readDec 26, 2017

As the Android programming is getting more and more exciting, I decided to try out bunch of the coolest things that are recently available for developers. For me, Reactive Extensions and Android Architecture Components are definitely one of those.

I decided to create some easy-peasy Android app, in which I can test out sort of things and concepts. Those were:

Those tools will be the foundations of the ShoppingListApp that I’ve made in last week. It will provide features like adding/archivising the shopping lists created by user and adding/removing the wanted element to them by swiping it. I provided the link to the source code on the end of this arcictle if you will be interested. So let’s dive into it!

Firstly, we must add the neccessary dependencies for our tools in the app build.gradle. At this time it can look like this:

// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:1.0.0"
annotationProcessor "android.arch.lifecycle:compiler:1.0.0"

// RxJava
implementation 'io.reactivex.rxjava2:rxjava:2.1.5'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'

// Room
implementation "android.arch.persistence.room:runtime:$roomVersion"
implementation "android.arch.persistence.room:rxjava2:$roomVersion"
kapt "android.arch.persistence.room:compiler:$roomVersion"
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.2.10"
androidTestImplementation "android.arch.persistence.room:testing:$roomVersion"

Let’s see how the Room entities are constructed:

@Entity(tableName = "shopping_list")
data class ShoppingList(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val id: Int = 0,
@ColumnInfo(name = "name")
val name: String,
@ColumnInfo(name = "is_archived")
val isArchived: Boolean,
@ColumnInfo(name = "timestamp")
val timestamp: Date,
@ColumnInfo(name = "items")
val items: ArrayList<ShoppingListItem>
)

data class ShoppingListItem(
val name: String,
val isCompleted: Boolean,
val timestamp: Date
)

There is one Entity which contains not only some primitive types, but also a list of objects. For this example i decided to not use the Relation annotation, but to use TypeConverters with Gson library to just store this collection as a String in my database and when i want to fetch the actual list of objects this String will be converted with the helpful hand of Gson to the actual list of objects. Here is how it can look like:

@TypeConverter
fun stringToShoppingListItems(json: String): ArrayList<ShoppingListItem> {

val gson = Gson()
val shoppingListItems : ArrayList<ShoppingListItem> = gson.fromJson(json, object : TypeToken<ArrayList<ShoppingListItem>>() {}.type)

return shoppingListItems
}

@TypeConverter
fun shoppingListItemsToString(list: ArrayList<ShoppingListItem>): String {
val gson = Gson()
val type = object : TypeToken<ArrayList<ShoppingListItem>>() {

}.type
return gson.toJson(list, type)
}

Let’s look on the DAO:

@Dao
interface ShoppingListDao {
@Query("SELECT * FROM shopping_list where id = :id limit 1")
fun getShoppingList(id: Int): Flowable<ShoppingList>

@Query("SELECT * FROM shopping_list where not is_archived")
fun getActiveShoppingLists(): Flowable<List<ShoppingList>>

@Query("SELECT * FROM shopping_list where is_archived")
fun getArchivedShoppingLists(): Flowable<List<ShoppingList>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertShoppingList(shoppingList: ShoppingList): Long

@Update
fun updateShoppingList(shoppingList: ShoppingList)

@Query("UPDATE shopping_list SET is_archived = 1 where id = :id")
fun archiveShoppingList(id: Int)

@Query("UPDATE shopping_list SET is_archived = 0 where id = :id")
fun reArchiveShoppingList(id: Int)
}

Room easy integrates with RxJava2 types such as Flowable, so we can make use of it and listen for updates every time there will be a change in our Query in the database. Of course this can be also achived with the LiveData from Android Architecture Components.

Let’s take a look on some methods of the ViewModel. The ViewModel will be provided from the ViewModelFactory, meaning that there will be one ViewModel for 3 screens that are in the app. The cool thing about it is that ViewModel must not know about the Views, it must only expose the data from datasource so that every View that are interested in receiving that data subscribes to the ViewModel’s methods.

Here is how the ViewModelFactory can look like:

class ViewModelFactory(private val dataSource: ShoppingListDao) : ViewModelProvider.Factory {

override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(ShoppingListViewModel::class.java)) {
return ShoppingListViewModel(dataSource) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}

And here are some of the methods that are in the ViewModel:

fun getShoppingLists(): Flowable<List<ShoppingList>> {
return dataSource.getActiveShoppingLists()
.map { t ->
t.sortedByDescending { it.timestamp }
}
}

fun getArchivedLists(): Flowable<List<ShoppingList>> {
return dataSource.getArchivedShoppingLists()
.map { t ->
t.sortedByDescending { it.timestamp }
}

}

Those methods returns only the ShoppingLists that are archived or not. I use one of them on each screen(one shows only active and other archived shopping lists). Those list are simply populated with RecyclerView adapter.

Lastly, let see briefly how we can subscribe to those methods in the Activities:

override fun onStart() {
super.onStart()
disposable.add(viewModel.getShoppingLists()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ t ->
shoppingList.clear()
t?.forEach {
val completed = it.items.filter(ShoppingListItem::isCompleted)
val item = ShoppingListDTO(it.id, it.name, it.timestamp, it.isArchived, completed.size, it.items.size)
shoppingList.add(item)
}

mAdapter?.notifyDataSetChanged()
}))
}

We see, that whenever there is a change in our database, we will be notified about it. We can then create some DTO object, which will be needed only for the View Activity and populate the wanted parameters to it, like for example size of the current shopping list element list and the number of completed elements(that were marked with checkboxes by user on the different screen)

We then can add those items to our adapter and notify about changes.

So that’s it! I really enjoyed working with those awesome tools. If you are interested you can look closely on the entire app here.

As i am just started to use those tools and also some patterns like MVVM i would love to here about improvements and also collaborate with you guys!

I hope you liked this post, becuase it is just my first post here on Medium. If you did, don’t forget to 👏 ! It will give me some motivation to make more :)

Cheers!

--

--