Open In App

Android – Build a Movie App using Retrofit and MVVM Architecture with Kotlin

Improve
Improve
Like Article
Like
Save
Share
Report

Model — View — ViewModel (MVVM) is the industry-recognized software architecture pattern that overcomes all drawbacks of MVP and MVC design patterns. MVVM suggests separating the data presentation logic(Views or UI) from the core business logic part of the application. 

The separate code layers of MVVM are:

  • Model: This layer is responsible for the abstraction of the data sources. Model and ViewModel work together to get and save the data.
  • View: The purpose of this layer is to inform the ViewModel about the user’s action. This layer observes the ViewModel and does not contain any kind of application logic.
  • ViewModel: It exposes those data streams which are relevant to the View. Moreover, it serves as a link between the Model and the View.

 

In this article, we will learn how we can build a simple movie detail app using MVVM architecture and Kotlin language. To build this application, we need MVVM architecture and Retrofit Library. Retrofit is a third-party library that helps us to make a network request in android. We will fetch data from The Movie Database Website (TMDB). To use this website, you need to log in and generate an API key. In this example, we are fetching the list of popular movies which we can get from this link.

Step by Step Implementation

Step 1: Create a New Project in Android Studio

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Kotlin as the programming language.

Step 2: Add Retrofit dependencies in Build.gradle(app) file

Kotlin




// retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'


Add View Model and Live Data dependencies in Build.gradle(app) file

Kotlin




def lifecycle_version = "2.6.0-alpha01"
// ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
// ViewModel utilities for Compose
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version")
// LiveData
implementation("androidx.lifecycle:lifecycle-live data-ktx:$lifecycle_version")


Add Glide Dependency in Build.gradle(app) file

Kotlin




implementation 'com.github.bumptech.glide:glide:4.13.2'


Glide library helps in image processing in android.

Step 3: Enable View Binding

To enable view binding add this code inside the android {} block in build.gradle(app) file

Kotlin




buildFeatures {
    viewBinding = true
}


Step 4: Generate data classes

Copy the response that you get from API

JSON Response

 

Right-click on the root package and select New->Kotlin-data class from JSON

If you don’t have this plugin, go to File -> Settings -> Plugin and install JSON to Kotlin Plugin. Copy the JSON result and paste it. Give this file a suitable name after that two data classes will be generated.

Kotlin




data class Movies(
    val page: Int,
    val results: List<Result>,
    val total_pages: Int,
    val total_results: Int
)


Kotlin




data class Result(
    val adult: Boolean,
    val backdrop_path: String,
    val genre_ids: List<Int>,
    val id: Int,
    val original_language: String,
    val original_title: String,
    val overview: String,
    val popularity: Double,
    val poster_path: String,
    val release_date: String,
    val title: String,
    val video: Boolean,
    val vote_average: Double,
    val vote_count: Int
)


Right Click on the Root package and create an interface MovieApi

Kotlin




interface MovieApi {
      @GET("popular?")
      fun getPopularMovies(@Query("api_key") api_key : String) : Call<Movies>      
}


Create a Retrofit Instance

Kotlin




object RetrofitInstance {
    val api : MovieApi by lazy {
        Retrofit.Builder()
            .baseUrl("https://api.themoviedb.org/3/movie/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(MovieApi::class.java)
    }
}


Step 5: Design layout Files

Navigate to the app > res > layout > activity_main.xml and add the below code to that file. Below is the code for the activity_main.xml file. Comments are added inside the code to understand the code in more detail.

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_movies"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        tools:listitem="@layout/movie_layout">
    </androidx.recyclerview.widget.RecyclerView>
    
</androidx.constraintlayout.widget.ConstraintLayout>


Since we are using recycler view we need to create a new layout file for that go to res->layout and create a new Layout resource file named movie_layout.

movie_layout.xml file

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    
    <ImageView
        android:id="@+id/movieImage"
        android:layout_width="200dp"
        android:layout_height="200dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:scaleType="fitCenter"
        android:src="@color/teal_200">
    </ImageView>
    
    <TextView
        android:id="@+id/movieName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/movieImage"
        android:textSize="30dp"
        android:text="Movie Name"
        android:textAlignment="center"
        android:textColor="@color/black"
        android:textStyle="bold"
        android:layout_marginTop="5dp">      
    </TextView>
    
</androidx.constraintlayout.widget.ConstraintLayout>


Step 6: Create a Movie Adapter class for RecyclerView

Kotlin




class MovieAdapter : RecyclerView.Adapter<MovieAdapter.ViewHolder>() {
    private var movieList = ArrayList<Result>()
    fun setMovieList(movieList : List<Result>){
        this.movieList = movieList as ArrayList<Result>
         notifyDataSetChanged()
    }
    class ViewHolder(val binding : MovieLayoutBinding) : RecyclerView.ViewHolder(binding.root)  {}
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
          MovieLayoutBinding.inflate(
              LayoutInflater.from(
                  parent.context
              )
          )
        )
    }
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
   Glide.with(holder.itemView)
    .load("https://image.tmdb.org/t/p/w500"+movieList[position].poster_path)
    .into(holder.binding.movieImage)
holder.binding.movieName.text = movieList[position].title
    }
    override fun getItemCount(): Int {
       return movieList.size
    }


Step 7: Create Movie View Model

Since we are using MVVM architecture we need to create a View Model Class with live-data in it.

Kotlin




class MovieViewModel : ViewModel() {
    private var movieLiveData = MutableLiveData<List<Result>>()
  fun getPopularMovies() {
      RetrofitInstance.api.getPopularMovies("69d66957eebff9666ea46bd464773cf0").enqueue(object  : Callback<Movies>{
          override fun onResponse(call: Call<Movies>, response: Response<Movies>) {
                    if (response.body()!=null){
                        movieLiveData.value = response.body()!!.results
                    }
              else{
                  return
                    }
          }
          override fun onFailure(call: Call<Movies>, t: Throwable) {
                Log.d("TAG",t.message.toString())
          }
      })
  }
    fun observeMovieLiveData() : LiveData<List<Result>> {
        return movieLiveData
    }
}


Step 8: Working with the MainActivity.kt file

Go to the MainActivity.kt file and refer to the following code. Below is the code for the MainActivity.kt file. Comments are added inside the code to understand the code in more detail.

Kotlin




class MainActivity : AppCompatActivity() {  
    private lateinit var binding : ActivityMainBinding
    private lateinit var viewModel: MovieViewModel
    private lateinit var movieAdapter : MovieAdapter
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        prepareRecyclerView()
        viewModel = ViewModelProvider(this)[MovieViewModel::class.java]
        viewModel.getPopularMovies()
        viewModel.observeMovieLiveData().observe(this, Observer { movieList ->
          movieAdapter.setMovieList(movieList)
        })
}
      
private fun prepareRecyclerView() {
        movieAdapter = MovieAdapter()
        binding.rvMovies.apply {
          layoutManager = GridLayoutManager(applicationContext,2)
           adapter = movieAdapter
        }
    }
}


Output:

Final Output

Final Output



Last Updated : 27 Sep, 2022
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads