Basic MVVM Android App

Basic MVVM Android App

About App

We are building a simple MVVM (Model View View-Model) app which is using Rick and Morty API.

Basically we are separting our concerns.

lets say we want to change request method from GET to POST, we only have made changes in Network Layer.

  • First read the Rick and Morty Documentation

Retrofit

Retrofit is a type-safe REST client for Android and Java which aims to make it easier to consume RESTful web services.

  1. First, create a Retrofit Service

    • It defines how the app should talk to the server, in our case we are sending a GET request.

        interface RickAndMortyService {
            @GET("character/{character-id}")
            suspend fun getCharacterById(@Path(value = "character-id") characterId: Int): Response<GetCharacterByIdResponse>
        }
      
  1. Network layer

    • First, create a Moshi object to parse JSON

    • Create a Retrofit Object

    • create a Retrofit Service using our Service Interface

    • finally, create an API client that which app can use to talk to the API using the Retrofit service.

    object NetworkLayer {

    // Moshi to parse JSON into Java and Kotlin classes:
        val moshi = Moshi.Builder()
            .add(KotlinJsonAdapterFactory())
            .build()

        val retrofit: Retrofit = Retrofit.Builder()
            .baseUrl("https://rickandmortyapi.com/api/")
            .addConverterFactory(MoshiConverterFactory.create(moshi))
            .build()

        val rickAndMortyService: RickAndMortyService by lazy {
            retrofit.create(com.example.rickmorty.RickAndMortyService::class.java)
        }

        val apiClient = ApiClient(rickAndMortyService)
    }
  1. Create API Client

    • It has a method to retrieve the response from API.
    class ApiClient(private val rickAndMortyService: RickAndMortyService) {
        suspend fun getCharacterById(characterId: Int): Response<GetCharacterByIdResponse> {
            return rickAndMortyService.getCharacterById(characterId)
        }
    }

Repository

It has the logic to send a request to API and retrieve a response using Network Layer via the API client.

class SharedRepository {
    suspend fun getCharacterById(characterId: Int): GetCharacterByIdResponse? {
        val request = NetworkLayer.apiClient.getCharacterById(characterId)

        if (request.isSuccessful) {
            return request.body()
        }

        return null
    }
}

ViewModel

ViewModel business logic or screen level state holder, which has the state of the UI and it exposes the state to UI and listens for changes.


class SharedViewModel : ViewModel() {
    private val repository = SharedRepository()

    // to store retrieved data
    private val _characterByIdLiveData = MutableLiveData<GetCharacterByIdResponse?>()
    val characterByIdLiveData: LiveData<GetCharacterByIdResponse?> = _characterByIdLiveData

    fun refreshCharacter(characterId: Int) {
        viewModelScope.launch {
            val response = repository.getCharacterById(characterId)
            _characterByIdLiveData.postValue(response)
        }
    }
}
  • Use the LiveData variable to store the response as it is an Observable Data holder class and the observer will be notified when data changes.

Activity (UI)

Here we will observe the response stored in the LiveData object from ViewModel


viewModel.characterByIdLiveData.observe(this)
    // action to take while observing
}

Here is the Github Repository