Professional Documents
Culture Documents
Tutorial 07.fetch and Display A List of Items
Tutorial 07.fetch and Display A List of Items
Tutorial 7
Agenda
1. Open the starter project
2. Lecture recap
3. Set up Retrofit
4. Check sample data in Postman
5. Create classes for JSON serialization
6. Get all movies request with Retrofit
7. MovieRepository and getAllMovies request
8. ViewModel and fetch all movies from the repository
9. Link LiveData and Jetpack Compose state
1.4 Checkout the starter branch and launch the project in an emulator device.
2. Lecture recap
1. Name the UI layer components.
2. Name the Data layer components.
3. To which layer ViewModel belongs?
4. What is LiveData?
5. What are coroutines?
3. Set up Retrofit
3.1 Inside your module level gradle file find the “dependencies” section, add these
dependencies and sync the project with Gradle. These include all the dependencies you will
need for this tutorial (not only Retrofit library).
//ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2"
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
//Coroutine
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
// Networking
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.okhttp3:okhttp:4.11.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
//Livedata
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.2"
implementation "androidx.compose.runtime:runtime-livedata:1.5.4"
3.2 Inside the data package create a new package “network”
3.3 Inside the “network” package create a new class RetrofitInstance. Copy paste this
code inside the RetrofitInstance class (note that imports are included in the sample code):
package com.example.movielist.data.network
import com.google.gson.GsonBuilder
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
class RetrofitInstance {
companion object {
this.level = HttpLoggingInterceptor.Level.BODY
this.addInterceptor(interceptor)
.connectTimeout(20, TimeUnit.SECONDS)
}.build()
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.build()
5.1 Serialization and deserialization means converting an object into text format and vice
versa.
5.2 Inside the “network” package Create a new Kotlin data class MyListResponse:
package com.example.movielist.data.network
import com.google.gson.annotations.SerializedName
@SerializedName("code")
@SerializedName("status")
@SerializedName("message")
@SerializedName("data")
import com.google.gson.annotations.SerializedName
@SerializedName("id")
@SerializedName("title")
@SerializedName("description")
)
6. Get all movies request with Retrofit
6.1 Inside the network package create a new interface MovieService.
6.2 Inside the MovieService interface create a new Retrofit request for fetching a list of movies.
The keyword suspend means that this function has to be called within a coroutine (i.e.
asynchronously).
package com.example.movielist.data.network
import com.example.movielist.data.network.movie.MovieResponse
import retrofit2.http.GET
import retrofit2.http.Query
import com.example.movielist.data.network.MyListResponse
interface MovieService {
@GET("records/all")
6.3 Inside the RetrofitInstance.kt class include the reference to the MovieService like this
(note the code in bold):
...
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.build()
.create(MovieService::class.java)
}
7. MovieRepository and getAllMovies request
7.1 Inside the data package find and open the class MovieRepository.kt
7.2 It has the getMovieList() function which returns dummy data for the movies list.
7.3 Modify the getMoviesList() function like this:
package com.example.movielist.data
import com.example.movielist.models.Movie
class MovieRepository {
return movies
7.4 Next, modify the getMoviesList() function like the code below. Now it contains the
repository request to fetch list of all movies.
package com.example.movielist.data
import com.example.movielist.data.network.MyListResponse
import com.example.movielist.data.network.RetrofitInstance
import com.example.movielist.data.network.movie.MovieResponse
import com.example.movielist.models.Movie
class MovieRepository {
RetrofitInstance.movieService.getAllMovies("movie")
return movies
}
7.5 Let’s map the response data to our internal Movie class:
suspend fun getMovieList(): List<Movie> {
RetrofitInstance.movieService.getAllMovies("movie")
if (moviesFromResponse != null) {
if (movieFromResponse.description != null) {
movies.add(
Movie(
movieFromResponse.name.uppercase(),
movieFromResponse.description
return movies
}
7.6 Wrap the function logic inside a Try…Catch block, in case something goes wrong with the
network request:
suspend fun getMovieList(): List<Movie> {
try {
RetrofitInstance.movieService.getAllMovies("movie")
if (moviesFromResponse != null) {
if (movieFromResponse.description != null) {
movies.add(
Movie(
movieFromResponse.name.uppercase(),
movieFromResponse.description
ex.printStackTrace()
return movies
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.movielist.data.MovieRepository
import kotlinx.coroutines.launch
class MovieListViewModel(
) : ViewModel() {
fun getAllMovies() {
viewModelScope.launch {
}
8.4 Create a new LiveData object at the top of the class:
package com.example.movielist.list
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.movielist.data.MovieRepository
import com.example.movielist.models.Movie
import kotlinx.coroutines.launch
class MovieListViewModel(
) : ViewModel() {
MutableLiveData<List<Movie>>()
fun getAllMovies() {
viewModelScope.launch {
8.5 Update the LiveData object from the result of the repository request inside the
getAllMovies() function:
fun getAllMovies() {
viewModelScope.launch {
moviesLiveData.value = movies
}
8.6 Include the init() method and call our getAllMovies() function there. This will make
the call happen on MoviesListViewModel() instance creation:
class MoviesListViewModel(private val movieRepository: MovieRepository) : ViewModel() {
MutableLiveData<List<Movie>>()
init {
getAllMovies()
fun getAllMovies() {
viewModelScope.launch {
moviesLiveData.value = movies
fun MoviesList(
) {
if (!movies.isNullOrEmpty()) {
LazyColumn(modifier = Modifier.fillMaxHeight()) {
MovieItem(movie = item)
})
import androidx.compose.runtime.livedata.observeAsState
9.4 Launch the application in the emulator to check the result. You should be able to see the list
of movies matching the database data.
NOTE: It is allowed to use this sample project as a template for your CW Application part