How to use Moshi serialiizer with retrofit, mvvm, coroutine in Kotlin?

Q: Why Use Moshi?

Ans: Moshi is a JSON parsing and serialization library developed by Square. It is a popular choice for JSON serialization in Kotlin projects for a few reasons:

  1. Kotlin support: Moshi has excellent support for Kotlin-specific features like data classes, sealed classes, and default parameter values. It can automatically handle the serialization and deserialization of these types without the need for custom adapters.
  2. Performance: Moshi is designed to be fast and lightweight. It uses code generation to avoid reflection-based deserialization, which can improve performance in some cases.
  3. Customization: Moshi is highly customizable, allowing you to add custom type adapters for handling non-standard types or tweaking the serialization and deserialization behavior for specific cases.
  4. Interoperability: Moshi is built on top of Okio, a utility library for working with I/O streams. This makes it easy to use Moshi with other libraries in the Okio ecosystem, like OkHttp and Retrofit.

Enough talking let’s dive deep to code!

1. First, you will need to include the following dependencies in your project:

// Retrofit for making HTTP calls
implementation "com.squareup.retrofit2:retrofit:2.9.0"

// Moshi converter for parsing JSON responses
implementation "com.squareup.retrofit2:converter-moshi:2.9.0"

// Moshi library for JSON parsing and serialization
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"

// Moshi adapters for common data types like Java 8 dates and times
implementation "com.squareup.moshi:moshi-adapters:1.13.0"

// OkHttp for HTTP request/response handling
implementation "com.squareup.okhttp3:okhttp:4.9.3"

// OkHttp logging interceptor for debugging HTTP traffic
implementation "com.squareup.okhttp3:logging-interceptor:4.9.1"

// Kotlin coroutines for asynchronous programming
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"

// Android Architecture Components for lifecycle management
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"

2. Once you have added these dependencies, you can start by creating a Retrofit instance with Moshi as the JSON converter factory. Here’s an example:

val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()

val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(OkHttpClient.Builder().addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}).build())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()

val apiService = retrofit.create(ApiService::class.java)

In this example, we are creating a Moshi instance with the KotlinJsonAdapterFactory, which is needed to properly deserialize Kotlin classes. We then create a Retrofit instance with MoshiConverterFactory as the JSON converter factory.

3. Next, let’s create a data class that we want to serialize and deserialize. Here’s an example:

data class User(
val id: Int,
val name: String,
val email: String
)

4. Now, let’s create an interface for our API service with a method that returns a User object. Here's an example:

interface ApiService {
@GET("users/{id}")
suspend fun getUser(@Path("id") id: Int): User
}

We are using the @GET annotation to specify the HTTP method and endpoint. We are also using the @Path annotation to specify a dynamic path parameter.

5. Finally, let’s create a ViewModel that uses the API service to fetch a User object. Here's an example:

class UserViewModel : ViewModel() {

private val apiService = retrofit.create(ApiService::class.java)

private val _user = MutableLiveData<User>()
val user: LiveData<User> = _user

fun getUser(id: Int) {
viewModelScope.launch {
try {
val result = apiService.getUser(id)
_user.postValue(result)
} catch (e: Exception) {
// handle error
}
}
}
}

We are using viewModelScope from the kotlinx.coroutines library to launch a coroutine for making the API call. If the call is successful, we update the _user LiveData with the result. If there's an error, we handle it accordingly.

Now let’s show the data in a fragment(you can use activity as well).

6. Here MyFragment displays a list of data items in a RecyclerView. The data is obtained from a ViewModel using Retrofit, Moshi, and coroutines. The ViewModel retrieves the data from a remote API, parses the JSON response using Moshi, and exposes the data as a LiveData object that can be observed by the Fragment. The RecyclerView adapter is updated whenever the LiveData data changes, and the updated data is displayed in the RecyclerView. The MyAdapter and MyViewHolder classes are used to set up the RecyclerView and define how the data items should be displayed in the list.

class MyFragment : Fragment() {

// Get an instance of the ViewModel
private val viewModel: MyViewModel by viewModels()

// Declare a reference to the RecyclerView
private lateinit var recyclerView: RecyclerView

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_my, container, false)

// Find the RecyclerView in the layout
recyclerView = view.findViewById(R.id.recyclerView)

// Set up the RecyclerView adapter
val adapter = MyAdapter()

// Set the adapter on the RecyclerView
recyclerView.adapter = adapter

// Observe the ViewModel data and update the adapter
viewModel.data.observe(viewLifecycleOwner) { newData ->
adapter.submitList(newData)
}

return view
}
}

class MyAdapter : ListAdapter<MyData, MyViewHolder>(MyData.DiffCallback) {
// onCreateViewHolder() and onBindViewHolder() implementations
}

class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
// Initialize the view elements in the ViewHolder
}

This code sets up a RecyclerView in a Fragment and observes the data in the ViewModel. Whenever the ViewModel data changes, the RecyclerView adapter is updated with the new data. You would need to create the layout file for the RecyclerView item view (e.g. my_item_layout.xml) and update the MyViewHolder class to reflect the view elements in that layout.

And that’s it! With these components in place, you should be able to use Moshi with Retrofit, MVVM, and Coroutines to fetch and deserialize JSON data.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Kashif Ahmad Sr.Android Developer @techeasesol.com
Kashif Ahmad Sr.Android Developer @techeasesol.com

Written by Kashif Ahmad Sr.Android Developer @techeasesol.com

Sr. Android Developer(Kotlin || Java || Flutter)| Co-Founder techeasesol.com || Semantic Digitisation Expert

No responses yet

Write a response