Exploring Kotlin using Android Architecture Components (and vice versa)

Recently, after all this time listening and reading about Kotlin I decided that it was time to give it a try. The best way to do it, I thought, was to write a new Android app from the beginning. Sure, Kotlin was designed to be “multi-platform” from the beginning (JVM, Server, JS, Native) but currently, it shines in the Android dev world.

The only issue was that since my last involvement with Android the new Android Architecture Components (AAC) had been introduced by Google and I didn’t have the chance to give them a try either.

Now, I had 2 new shiny things to try out and to be honest it seemed to be a bit overwhelming at first. If you are in a similar position, I urge you to fear not. These are not just “shiny things” but will actually make you more productive if you have them in your toolkit.

I will not repeat what you might have already read/heard about the merits of Kotlin. I will try to guide you through AAC using Kotlin and explain as much Kotlin features/syntax as possible in the way. I don’t expect to fully understand Kotlin or AAC by the end of this post, but you will have a taste of what Kotlin and AAC are so when you have the time to take a deeper look.

Stick to the end that I have some useful links for diving deeper into Kotlin and AAC.

Lifecycle

The Android System can destroy and recreate an app’s components (e.g. Activity, Fragment) due to memory constraints (e.g. low on RAM) or configuration changes (e.g. switch from Portrait -> Landscape). You might know that Android components have complicated life cycles. Each component has its own different lifecycle (e.g. fragments, activities) and this makes things even more complicated to reason about.

These complicated lifecycles make lives of developers difficult because you might start an operation on an Activity (e.g. network request) and while in progress the Activity can be destroyed and another will be recreated. You will need to show the results of the operation in the new Activity. AAC introduced components (i.e. Activities, Fragments) that are Lifecycle aware. This makes it easier to reason about and not having to manually keep track in which lifecycle state each component is.

LiveData

This provides an async mechanism to provide data changes using the Observer pattern. It’s a very similar, yet way simplified version, of the popular and very powerful RxJava. The big difference is that LiveData is respecting the Lifecycle of the Android component that is attached to and delivers data changes only when the component is in “active” state (e.g. when the activity is stopped/destroyed will not try to deliver changes). This avoids memory leaks, crashes due to stopped activities and other issues. Too much talk, let’s see a code example:


private val username
= MutableLiveData<String>()

/* Called on app launch */
fun initNetworkRequest() {
/* expensive operation, e.g. network request */
username.value = "Peter"
}
fun getUsername(): LiveData<String> {
return username
}
// --------------------------------------------------------
/* Called on Activity creation */
getUsername().observe(this, Observer { user -> Log.d(TAG, user) })

With this approach, even if the Activity is destroyed by the system and another one is created before the request finishes, the correct one will observe and show the results.

Kotlin

private val username = MutableLiveData<String>()
  • New instances are constructed without the new keyword, just their class name.
  • val means final. This variable cannot be reassigned.
  • var for a variable that can be reassigned.
  • Kotlin is strongly typed, i.e. each variable has an explicit type. If it can be inferred then you can avoid writing it, like in this case. Otherwise, you can also write:
    val username: MutableLiveData<String> = MutableLiveData<String>
  • Visibility modifier private means the same thing as in Java, only visible within this class. Most of the other modifiers are the same. The default visibility, if there is no modifier specified, is public.
fun getUsername(): LiveData<String> {
  • Use fun keyword for method declaration.
  • The return type comes at the end of the signature.
  • There is no void in Kotlin (it’s Unit), just omit the type to return nothing like in:
    fun initNetworkRequest()
username.value = "Peter"
  • Kotlin is smart enough to convert Java getValue/setValue to properties like this (i.e. when running this code you are calling username.setValue() behind the scenes).
getUsername().observe(this, Observer { user -> Log.d(TAG, user) })
  • Here an anonymous Observer class is used and Kotlin is automatically doing the work of figuring out which method to override (take a look at the Java code to help you understand what Kotlin is doing).

ViewModel

The ViewModel is used to persist configuration changes (i.e. Portrait -> Landscape). You can save the logical state of the UI in the ViewModel and decouple it from the Android component (i.e. Activity, Fragment) that can be destroyed at any time. If we combine the previous code with this one:

class MainViewModel : ViewModel() {

private val username = MutableLiveData<String>()

fun initNetworkRequest() {
/* expensive operation, e.g. network request */
username.value = "Peter"
}

fun getUsername(): LiveData<String> {
return username
}
}
// --------------------------------------------------------
/* Called on Activity onCreate() */
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
getUsername().observe(this, Observer { text -> Log.d(TAG, text) })

/* Called if there is no active network request */
viewModel.initNetworkRequest()

No matter how many times the Activity will be destroyed and recreated, the same ViewModel instance will be used. Take a look at the ViewModel lifecycle. Therefore, you can safely store the state of your UI and on recreation the new Activity will get the latest values.

Room

Room library provides an easy way to convert objects to data stored in a local SQLite database on the device. This is useful for persisting data across different app sessions and also for offline usage. Room has 3 main components that you need to define to use it:

@Entity
data class User(
@PrimaryKey(autoGenerate = true) val id: Int? = null,
@ColumnInfo(name = "username") val username: String) {
}

This class describes the data format (and how the underline SQL table is defined). This is what you will fill in order to save a new row in the database and how the data that are retrieved from the database will be returned to you.

@Dao
interface UserDao {

@Query("SELECT * FROM user")
fun all(): List<User>

@Insert
fun insertAll(users: List<User>): List<User>

@Insert
fun insert(user: User): Long

@Update
fun update(user: User)

@Delete
fun delete(user: User)
}

The Data Access Object defines how you access your data. Notice that this is just an interface with some annotated methods. Room will create the implementations based on what you provide in the annotated methods.

@Database(entities = arrayOf(User::class), version = 1)
abstract class Database : RoomDatabase() {

companion object {

private val DB_NAME = "UsersDatabase"

@Volatile private var INSTANCE: Database? = null

fun
getInstance(context: Context): Database =
INSTANCE ?: synchronized(this) {
INSTANCE
?: buildDatabase(context).also
{ INSTANCE = it }
}

private fun
buildDatabase(context: Context) =
Room.databaseBuilder(context.applicationContext,
Database::class.java, DB_NAME)
.build()
}
}

This the main access point for the database. It looks complicated because it was taken from a sample app from Google, but I find it more robust than the one provided the docs.

Kotlin

data class User(val id: Int? = null, val username: String)
  • data keyword is used for value classes that their aim is just to hold data. All the methods such equals()/hashCode()/toString()/copy() are created automatically.
  • Using the variables after the class name you declare them as (public) member variables. Since val is used these are immutable member variables.
  • Int? means that this variable is allowed to take null as its value. Without ‘?’ at the end of the variable type, null is not allowed.
companion object { 
  • This is similar to Java static. Of course, there are differences, such as that you can pass this object around and it can implement interfaces.
INSTANCE ?: synchronized(this) {
  • The ?: operator can be used to return a value if it’s not null, otherwise to return what is right of the operator. It replaces all the null-safe if statements in Java.

Afterword / Further reading

In case you are curious, the app I created is microtasks and I would be delighted to try it and give feedback (using the in-app “Send feedback” option :). Also, if you are looking to promote your apps using promo codes take a look at Promies!

For Kotlin, I had great fun using Kotlin Koans (available in-browser as well!) to learn the syntax and features interactively. Also, as a cheatsheet, I used the Learn X in Y minutes. Kotlin is a modern language with a lot of features. I did learn the basics but there are so many advanced features that I will be in learning mode for a while.

For Android Architecture Components, I relied mainly on the official documentation. I found it easy to follow and with good examples. Also, I found this Guide to App Architecture to be really good at giving you a high-level overview on how to structure your app.

I hope you found this useful! Happy coding using Kotlin and AAC! :)