Simple REST API Android App in Kotlin - Various HTTP Client Library Implementations
Tags: android, beginners, kotlin, jetpackcompose
Published: 2022-06-03 23:32:07

**How to use Retrofit, Moshi, Gson, Kotlin Serialization and Ktor client libraries to connect the REST API web services in Android app?**

I created this simple Android App (written in Kotlin to help me to understand different ways to connect to REST API web service using different HTTP Client libraries.

I also tried to measure the memory and performance of using these libraries.

![Simple_REST_API_Android_App_in_Kotlin_01.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1648540857292/3tveS1JRz.gif)

This is the REST API - [Meals Categories](https://www.themealdb.com/api/json/v1/1/categories.php) from TheMealDB that the app tries to retrieve. It returns in JSON format. 

The app is implemented with [MVVM](https://vtsen.hashnode.dev/mvc-vs-mvp-vs-mvvm-design-patterns), but the following only highlights the steps you need to do to build these HTTP client libraries. 

If you want to know the details, please refer to the source code provided at the end of this article.

## 1. Retrofit + Moshi

This is the first method I learned while creating this [Asteroid Rader App](https://vtsen.hashnode.dev/android-kotlin-developer-nanodegree-projects-review#heading-course-2-project-build-an-asteroid-radar) in one of my Android Kotlin Developer Nanodegree Projects.

- Retrofit is the HTTP client library to connect to the REST API web service 
- Moshi  is the library to parse JSON responses into Kotlin data object

### Import Retrofit + Moshi Converter Libraries
```
def retrofit_version = "2.9.0"  
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"  
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"

def moshi_version = "1.12.0"  
implementation "com.squareup.moshi:moshi:$moshi_version"  
implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"
```

### Create Data Class for Moshi

```kotlin
data class MoshiMealCategoriesResponse (
    val categories: List<MoshiMealCategory>
)

data class MoshiMealCategory(
    @Json(name="idCategory") val idCategory: String,
    val strCategory: String,
    val strCategoryDescription: String,
    val strCategoryThumb: String
)
```
 `@Json` is Moshi annotation and only is needed if your val name is different from the JSON string.

### Define Retrofit + Moshi API Interface

```kotlin
interface RetrofitMoshiMealsApi {
    @GET("categories.php")
    suspend fun getMealCategories(): MoshiMealCategoriesResponse
}
```

### Build Retrofit + Moshi API 

```kotlin
class RetrofitMoshiMealsWebService {

    private val api: RetrofitMoshiMealsApi by lazy {
        createMealsApi()
    }

    suspend fun getMealCategories(): MoshiMealCategoriesResponse {
        return api.getMealCategories()
    }

    private fun createMealsApi(): RetrofitMoshiMealsApi {
        val moshi = Moshi.Builder()
            .add(KotlinJsonAdapterFactory())
            .build()

        val retrofit = Retrofit.Builder()
            .baseUrl(MainRepository.BASE_URL)
            .addConverterFactory(MoshiConverterFactory.create(moshi))
            .build()

        return retrofit.create(RetrofitMoshiMealsApi::class.java)
    }
}
```

## 2. Retrofit + Gson

Similar to Moshi, Gson is an open-source Java library to serialize and deserialize JSON to Kotlin data objects.

Since Moshi import has already been shown above, I'm not going to show it here again.

### Import Gson Converter Library
```
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
```

### Create Data Class for Gson

```kotlin
data class GsonMealCategoriesResponse (
    val categories: List<GsonMealCategory>
)

data class GsonMealCategory(
    @SerializedName("idCategory") val idCategory: String,
    val strCategory: String,
    val strCategoryDescription: String,
    val strCategoryThumb: String
)
```
`@SerializedName` is Gson annotation, which is similar to `@Json` in Moshi annotation if your JSON string is different from the val name.

Technically I can share the same data class for all these different JSON parser implementation, but I think it is cleaner to separate as it requires different annotations for different parser libraries.

### Define Retrofit + Gson API Interface
```kotlin
interface RetrofitGsonMealsApi {
    @GET("categories.php")
    suspend fun getMealCategories(): GsonMealCategoriesResponse
}
```
### Build Retrofit + Gson API 

```kotlin
class RetrofitGsonMealsWebService {  
  
    private val api: RetrofitGsonMealsApi by lazy {  
        createMealsApi()  
    }  
  
    suspend fun getMealCategories(): GsonMealCategoriesResponse {  
        return api.getMealCategories()  
    }  
  
    private fun createMealsApi(): RetrofitGsonMealsApi {  
  
        val gsonConverterFactory = GsonConverterFactory.create()  
  
        val retrofit = Retrofit.Builder()  
            .baseUrl(MainRepository.BASE_URL)  
            .addConverterFactory(gsonConverterFactory)  
            .build()  
  
        return retrofit.create(RetrofitGsonMealsApi::class.java)  
    }  
}
```

## 3. Retrofit + Kotlin Serialization
Similar to Moshi and Gson, Kotlin Serialization is an official Kotlin library that can be used to serialize and deserialize JSON to Kotlin data objects. 

One of the recommendations I got in the [Android Kotlin Developer NonoDegree](https://vtsen.hashnode.dev/is-it-worth-to-pay-for-android-kotlin-developer-nanodegree) is to use Kotlin Serialization. The memory and performance are better because it doesn't use reflection. 

### Add Kotlin Serialization Plugin
Add this in `app\build.gradle`
```
plugins {  
  ...
  import project.  id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlin_version"  
}
```
Make sure you update the `build.gradle`  at the **app** level and not at the **project** level.  If you see this warning below, you likely update the wrong `build.gradle` file (i.e. project level).
```
Warning:(5, 1) kotlinx.serialization compiler plugin is not applied to the module, so this annotation would not be processed. Make sure that you've setup your buildscript correctly and re-import project.
``` 
It took me a while to figure out I updated the wrong `build.gradle`. It compiled fine but failed at run time. Don't make the same mistake I did.


### Import Kotlin Serialization Library
```
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"
```
### Import Kotlin Serialization Converter + okhttp3 Libraries

There is no official Kotlin Serialization Converter for Retrofit from Squareup, and we're using the one from Jake Wharton.
```
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"  
implementation "com.squareup.okhttp3:okhttp:4.9.3"
```
`okhttp3` is required for `"application/json".toMediaType() ` usage. See **Build Retrofit + Kotlin Serialization** section below.

### Create Data Class for Kotlin Serialization

```kotlin
@Serializable  
data class KotlinSerdesMealCategoriesResponse (  
    val categories: List<KotlinSerdesMealCategory>  
)

@Serializable  
data class KotlinSerdesMealCategory(  
    @SerialName("idCategory") 
    val idCategory: String,  
    val strCategory: String,  
    val strCategoryDescription: String,  
    val strCategoryThumb: String  
)
```
Similar to `@Json` (Moshi) and `@SerializedName` (Gson), `@SerialName` is used for Kotlin Serialization. Please note that you need to annotate the class with `@Serializable` in order to use the Kotlin Serialization.

### Define Retrofit + Kotlin Serialization API Interface
```kotlin
interface RetrofitKotlinSerdesMealsApi {  
    @GET("categories.php")  
    suspend fun getMealCategories(): KotlinSerdesMealCategoriesResponse  
}
```

### Build Retrofit + Kotlin Serialization
```kotlin
class RetrofitKotlinSerdesMealsWebService  {  
  
    private val api: RetrofitKotlinSerdesMealsApi by lazy {  
        createMealsApi()  
    }  
  
    suspend fun getMealCategories(): KotlinSerdesMealCategoriesResponse {  
        return api.getMealCategories()  
    }  
  
    @OptIn(ExperimentalSerializationApi::class)  
    private fun createMealsApi(): RetrofitKotlinSerdesMealsApi {  
    
        val contentType = "application/json".toMediaType()  
        val retrofit = Retrofit.Builder()  
            .baseUrl(MainRepository.BASE_URL)  
            .addConverterFactory(Json.asConverterFactory(contentType))  
            .build()  
  
        return retrofit.create(RetrofitKotlinSerdesMealsApi::class.java)  
    }  
}
```
Please note that you need to add `@OptIn(ExperimentalSerializationApi::class)` in order to use the converter library. You also need to add the opt-in compiler argument in your `build.gradle` module level file.

```
android {

    ...

    tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
        kotlinOptions {
            freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
        }
    }
}

```
You can also refer to the following blog post:
- [How to add opt-in compiler argument in build.gradle?](https://vtsen.hashnode.dev/how-to-add-opt-in-compiler-argument-in-buildgradle)

## 4. Ktor Client + Kotlin Serialization

Ktor client is a multiplatform HTTP client library. 

### Import Ktor Client with Kotlin Serialization Libraries
```
def ktor_version = "1.6.8"  
implementation "io.ktor:ktor-client-core:$ktor_version"  
implementation "io.ktor:ktor-client-cio:$ktor_version"  
implementation "io.ktor:ktor-client-serialization:$ktor_version"
```

### Create Ktor Client and Implement API Interface
The data class is exactly the same with the Kotlin Serialization data class above <enter link here>. So I'm not going to show here again.

To use Ktor Client, we don't really need to define the interface as required by Moshi. You can just call `ktorHttpClient.get("URL here")`  API directly.

```kotlin
class KtorKotlinSerdesMealsWebService {  
  
    private val ktorHttpClient = HttpClient {  
	    install(JsonFeature) {  
        serializer = KotlinxSerializer()  
    }  
 }
   
 suspend fun getMealCategories(): KotlinSerdesMealCategoriesResponse {  
        return ktorHttpClient.get("${MainRepository.BASE_URL}categories.php")  
    }  
}
```

## Memory and Performance
I added this `Enable Performance Test` check box to the main screen. When it is checked, it will call the API 10 times for performance testing.

![Simple_REST_API_Android_App_in_Kotlin_02.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1648723394879/A5Oc6KKoy.png)

I ran some memory and performance tests and here are the results. I ran a couple of times and took the average. I also restarted the app to run HTTLP client library independently, so the results won't be overlapped.

|HTTP Client Library |Memory Usage | Performance                         
|--------------------|-------------|------------|
|Retrofit + Moshi |22 M bytes | 4.8 seconds |
|Retrofit + Gson    |19 M bytes | 4.9 seconds |
|Retrofit + Kotlin Serialization |20 M bytes| 4.9 seconds |
|Ktor Client + Kotlin Serialization | 22 M bytes| 10.8 seconds |

Memory and performance for **Retrofit + Gson** and **Retrofit + Kotlin Serialization** are similar. **Retofit + Moshi** uses slightly more memory with similar performance, but it could be just a false positive.

But, what happens to **Ktor Cilent**? Ktor Client's performance is around 2x slower!

> **Note:** The performance results above may not be updated. I didn't rerun it after I have upgraded the example to the latest library versions. So you may want to run it yourself to confirm what I claimed is still true.

## Conclusion
Before I ran the memory and performance, I had an impression **Ktor Client + Kotlin Serialization** must be the best option, but it turned out to be the worst! Maybe it is because of multiplatform overhead?

Also, the claim for Kotlin Serialization uses less memory and faster is probably not true. It is about the same as Moshi and Gson or I did not run the test, right?

It is very obvious **the choice is Retrofit**. 

Personally, I will probably choose 
- Moshi over Gson because Moshi is a newer library than Gson
- Moshi over Kotlin Serialization because Retrofit Kotlin Serialization Converter is NOT an official library(not part of the Squareup libraries). 

Given this little research that I have done, my go-to is **Retrofit + Moshi**. 

## Source Code
GitHub Repository: [Demo_SimpleRestAPI](https://github.com/vinchamp77/Demo_SimpleRestAPI)

---

*Originally published at [https://vtsen.hashnode.dev](https://vtsen.hashnode.dev/simple-rest-api-android-app-in-kotlin-various-http-client-library-implementations)*.