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.
This is the REST API - Meals Categories from TheMealDB that the app tries to retrieve. It returns in JSON format.
The app is implemented with MVVM, 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 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
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
interface RetrofitMoshiMealsApi {
@GET("categories.php")
suspend fun getMealCategories(): MoshiMealCategoriesResponse
}
Build Retrofit + Moshi API
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
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
interface RetrofitGsonMealsApi {
@GET("categories.php")
suspend fun getMealCategories(): GsonMealCategoriesResponse
}
Build Retrofit + Gson API
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 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
@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
interface RetrofitKotlinSerdesMealsApi {
@GET("categories.php")
suspend fun getMealCategories(): KotlinSerdesMealCategoriesResponse
}
Build Retrofit + Kotlin Serialization
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:
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 . 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.
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.
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
Originally published at https://vtsen.hashnode.dev.
Top comments (2)
I would be curious to see how the ktor client does if you use the OkHttp engine.
Cool, yeah thanks for the suggestion! I think Retrofit uses OKHttp underneath by default. So in theory, the OkHttp should be better than Ktor Client.
P/S: I haven't run this for a while. Maybe the Ktor Client's performance may have been improved over time.