Kotlin is not really a new language- it’s over five years old, and quite mature. You can check here for more information on the new adoption.

I plan to share some “how-tos” around using Kotlin in Android development.

This is going to be a first in the series of said “how-tos.” Retrofit is a very popular networking library by the good folks at Square, and it is widely used in the dev community. Even Google uses it in their code samples.

In this post, I will be talking about how do REST API consumption in your applications using Retrofit + Kotlin + RxJava. We will be making use of the Github API to fetch the list of Java developers in Lagos, Nigeria. This post is going to highlight some features of the programming language and how we can apply them to something we Android developers do daily - make API calls.

This assumes that you have your development setup ready for Kotlin. In Android Studio 3.0 (Preview), there is now a native support for Kotlin, without manually installing plugins. Check here to install the preview. However, if you don’t want to install the new Android Studio Preview (I recommend you sideload it with your stable version), you can set up your current Android Studio using this guide.

1. Add Dependencies

To use Retrofit, you need to add the dependencies to the app-module build.gradle file:

dependencies {
    // retrofit
    compile "com.squareup.retrofit2:retrofit:2.3.0"
    compile "com.squareup.retrofit2:adapter-rxjava2:2.3.0"
    compile "com.squareup.retrofit2:converter-gson:2.3.0"

    // rxandroid
    compile "io.reactivex.rxjava2:rxandroid:2.0.1"
}

The first dependency in the block above is the retrofit dependency, the second is the RxJava2 adapter, which will help us make our calls reactive - using RxJava2. The third is the json converter that will handle the deserialization and serialization of the request and response bodies to and from JSON format. The last dependency is RxAndroid which will help us with Android specific bindings for RxJava2.

Typically, the next step is to create the data classes which are POJOs (Plain Old Java Objects) that will represent the responses of the API calls we’re going to make. With the Github API we’re considering in this post, we will have users as entities as well as other metadata about the search results.

A typical Java class that will hold the User data will look like we have in this Github gist. For the convenience of the readers, I won’t post the class here, but it is a 154-line Java class, describing the User entity. Here is one of the wins for Kotlin - it is much less verbose than Java. We can reproduce the same class in a readable format in less than twenty lines.

data class User(
        val login: String,
        val id: Long,
        val url: String,
        val html_url: String,
        val followers_url: String,
        val following_url: String,
        val starred_url: String,
        val gists_url: String,
        val type: String,
        val score: Int
)

Data Classes in Kotlin

We have this concise version of the User entity with Kotlin, thanks to what is called data class in Kotlin. Data classes in Kotlin are classes that are designed specifically for classes that do nothing but hold data.

The Kotlin compiler automatically helps us with implementing equals(), hashCode() and toString() methods on the class and that makes the code even shorter, because we don’t need to do that on our own. We can override the “default” implementation of any of these methods by defining the method.

A nifty feature is that we can create our search results in one single Kotlin file - say SearchResponse.kt. Our final search response class will contain all the related data classes and look like we have below:

SearchResponse.kt:

data class User(
        val login: String,
        val id: Long,
        val url: String,
        val html_url: String,
        val followers_url: String,
        val following_url: String,
        val starred_url: String,
        val gists_url: String,
        val type: String,
        val score: Int
)

data class Result (val total_count: Int, val incomplete_results: Boolean, val items: List<User>)

3. Create the API Service Interface

The next step, as we usually do in Java, is to create the API interface which we will use to make requests and get responses via retrofit. Typically, in Java, I like to create a convenience “factory” class that creates the API service when it is needed and I would do something like this:

GithubApiService.java:

public interface GithubApiService {

    @GET("search/users")
    Observable<Result> search(@Query("q") String query, @Query("page") int page, @Query("per_page") int perPage);

    /** * Factory class for convenient creation of the Api Service interface */
    class Factory {

        public static GithubApiService create() {
            Retrofit retrofit = new Retrofit.Builder()
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl("https://api.github.com/")
                    .build();

            return retrofit.create(GithubApiService.class);
        }
    }
}

To use this interface, we then make calls like this:

GithubApiService apiService = GithubApiService.Factory.create();
apiService.search(/** search parameters go in here **/);

To replicate this kind of behavior in Kotlin, we would have an equivalent GithubApiService.kt Kotlin interface that would look like this:

GithubApiService.kt:

interface GithubApiService {

    @GET("search/users")
    fun search(@Query("q") query: String,
               @Query("page") page: Int,
               @Query("per_page") perPage: Int): Observable<Result>

    /** * Companion object to create the GithubApiService */
    companion object Factory {
        fun create(): GithubApiService {
            val retrofit = Retrofit.Builder()
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl("https://api.github.com/")
                    .build()

            return retrofit.create(GithubApiService::class.java);
        }
    }
}

Usage of this interface and factory class will look like this:

val apiService = GithubApiService.create()
apiService.search(/* search params go in here */)

Note that we didn’t have to use the “name” of the companion object to reference the method, we only used the class name as the qualifier.

Singletons and Companion Objects in Kotlin

To understand what companion objects in Kotlin is, we must first understand what object declarations are in Kotlin. An object declaration in Kotlin is the way a singleton is made in Kotlin. Singletons in Kotlin is as simple as declaring an object and qualifying it with a name. For example:


object SearchRepositoryProvider {
    fun provideSearchRepository(): SearchRepository {
        return SearchRepository()
    }
}

Usage for the above object declaration is:

val repository = SearchRepositoryProvider.provideSearchRepository();

With this, we have been able to create a provider that will provide us with a repository instance (that will help us connect to the Github API via the GithubApiService).

The object declaration is lazily initialized when accessed the first time - the same way a Singleton works. Companion objects, however, are a type of object declaration that is qualified with the companion keyword. Companion objects can be likened to static methods or fields in Java. In fact, if you are referencing a companion object from Java, it would appear as a static method or field. The companion object is what is used in the GithubApiService.kt Kotlin version above.

Since we are trying to abstract our processes as much as possible (while leaving it as simple as possible), we can create a simple repository that handles calling the GithubApiService and builds the query string.

The query string matching our specification for this demo app (to find Java developers in Lagos) using the Github API is location:Lagos+language:Java, so we will create a method in the repository that will allow us build this string while taking the location and language as parameters. Our search repository will look like this:

class SearchRepository(val apiService: GithubApiService) {
    fun searchUsers(location: String, language: String): Observable<Result> {
        return apiService.search(query = "location:$location+language:$language")
    }
}

String Templates in Kotlin

In the block of code above, we have used a feature of Kotlin called “String templates” to build our query string. String templates start with the dollar sign - $ and the value of the variable following it is concatenated with the rest of the string. This is a similar feature to String interpolation in Groovy.

5. Make Request and Observe API Response Using RxJava

Now that we have configured our response classes, our repository interface to help us make the request, we can now make the request and retrieve the API response using RxJava. This step is similar to how we would do it in Java. In Kotlin code, it looks like this:

val repository = SearchRepositoryProvider.provideSearchRepository()
repository.searchUsers("Lagos", "Java")
        .observeOn(AndroidSchedulers.mainThread())
        .subscribeOn(Schedulers.io())
        .subscribe ({
            result ->
            Log.d("Result", "There are ${result.items.size} Java developers in Lagos")
        }, { error ->
            error.printStackTrace()
        })

With this, we have made our request and we can retrieve the response and do whatever we want with it.

Additional Kotlin Resources

In summary, in this post, we have looked at some cool features/properties of Kotlin language and how we can apply them in using Retrofit + RxJava for network calls.

These features include:

  • Data class.
  • Object declarations.
  • Companion objects.
  • String templates.
  • Interoperability with Java.

For the full project demo used in this tutorial, you can check out the source code.

In the future, I will write about other features of the language and give practical examples of how we can apply them in our day-to-day programming in Android development.

If you found this post useful, please share. Feel free to discuss or ask questions or make corrections in the comments below. Thanks for reading.