Skip to content
Nate Ebel edited this page Jul 21, 2022 · 6 revisions

🖥 Lab 12: Fetch data from a network

Use Retrofit and Moshi to load JSON data from a network api.

  • Retrofit is a popular HTTP client for Android and Java. It will generate a working network client based on service definition interfaces you provide it. In this lab, we will use Retrofit to fetch a list of study topics from a network api.
  • Moshi is a JSON serialization library that works very well with Kotlin. We can use Moshi, in conjunction with Retrofit, to deserialize our network responses.
  • Both Retrofit and Moshi use annotations to generate working implementations for us. Retrofit uses those annotations to inform its reflection-based processing of the service definition to provide a working network client. Moshi must use either an annotationProcessor such as kapt (kotlin annotation processor) or, as in the case of this project, ksp (kotlin symbol processing).

 

📝 Objectives

  1. Open AndroidManifest.xml and add <uses-permission android:name="android.permission.INTERNET" />

  2. Create a new package named network

  3. In the network package, create an interface named StudyGuideService

  4. Add a method to StudyGuideService called getTopics() that is a suspending function and returns a List<Topic>

    1. To make a Kotlin function a suspending function, add the suspend keyword before the fun keyword
    2. Suspending functions are a core part of Kotlin's coroutine apis.
    3. Suspending functions are functions that may be paused ("suspended") and resumed at a later point - without blocking the current Thread
    4. Suspending functions allow us to write asynchronous code in an imperative way. Our suspending function may be called, and then paused while it waits for some aysnc operation such as a network or database result. Once that result is returned, our suspending function may then continue on with the rest of its operations.
    5. For a more detailed example of suspending functions, see this guide.
  5. Add the following annotation to the Topic class: @JsonClass(generateAdapter = true)

    1. This annotation will indicate to Moshi that it should generate a Json adapter automatically
    2. Moshi is a serialization library that has strong support for Kotlin
    3. Using Moshi will help us deserialize network responses
  6. Add the base endpoint url to your user directory's .gradle/gradle.properties file

    1. Navigate to your computer's current user directory
    2. You'll want to open the .gradle directory.
    3. Open, or create if it doesn't exist, the gradle.properties file
    4. Within that file, add the following STUDY_GUIDE_SERVICE_URL=<end point url from instructor>
    5. Within Android Studio, re-sync your project, then rebuild your project
    6. This should result in a new BuildConfig property being generated that allows us to use this endpoint url without having to check it into our codebase
  7. We need to create an instance of Retrofit.Builder to generate a working network client

    1. Open StudyGuideService.kt
    2. Create a top-level property named studyGuideService and initialize it to Retrofit.Builder().build()
    3. Before the .build() call, add a call to baseUrl(BuildConfig.STUDY_GUIDE_SERVICE_URL)
    4. After setting the base url, add a json converter factory with .addConverterFactory(MoshiConverterFactory.create())
  8. After calling .build() on Retrofit.Builder, create an instance of the StudyGuideService interface by calling .create(StudyGuideService::class.java)

  9. Within the init{} of StudyGuideViewModel, replace the current implementation with one that uses the new Retrofit service to load the topics

 

✨ Challenges

Show a loading indicator while the study guide topics are loading

  1. You can add a new property to your UiState class to represent the loading state of the screen
  2. Make the loading property default to true
  3. In StudyGuideFragment update your layout, and code, to show an indeterminate progress bar (a progress bar that just spins indefinitely)
    1. In fragment_study_guide.xml add com.google.android.material.progressindicator.CircularProgressIndicator after the RecyclerView
    2. Constrain it to fit in the middle of the screen
    3. Add android:visibility="visible"
    4. Add android:indeterminate="true"
    5. Add app:indicatorColor="?colorPrimary" and app:trackColor="?colorSecondary" to update the styling

 

🖥 Lab 12 Hints: Fetch data from a network

 

💡 Useful Resources

  1. Connecting to the internet
  2. Retrofit: A type-safe http client for Android and Java
  3. Moshi

 

💡 How do I add the INTERNET permission to the manifest?

Open your AndroidManifest.xml file, and add the permission declaration as follows:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="dev.goobar.androidstudyguide">

  <uses-permission android:name="android.permission.INTERNET" />

  <application>

  ...

 </application>

</manifest>

 

💡 How do I generate BuildConfig.STUDY_GUIDE_SERVICE_URL?

The BuildConfig.STUDY_GUIDE_SERVICE_URL property is generated based on the presence of a local Gradle property. We don't just hardcode this value, because we want the url to be more secure to help avoid unwanted use. The app/build.gradle file is already configured to generate this value if STUDY_GUIDE_SERVICE_URL is present in either the project's gradle.properties file or your user directory's .gradle/gradle.properties file.

If you are having trouble generating this value, make sure that the Gradle property is set. It should look something like STUDY_GUIDE_SERVICE_URL="<service url>" If you don't know what the URL should be, contact the instructor.

 

💡 How do I create an instance of StudyGuideService using Retrofit?

val studyGuideService = Retrofit.Builder()
  .baseUrl(BuildConfig.STUDY_GUIDE_SERVICE_URL)
  .addConverterFactory(MoshiConverterFactory.create())
  .build()
  .create(StudyGuideService::class.java)

 

💡 How to use our service from the StudyGuideViewModel?

Update the init{} block of the view model to launch a new coroutine in a background Thread, make a call to studyGuideService.getTopics(), and finally update the UiState.

viewModelScope.launch(Dispatchers.IO) {
  val topics = studyGuideService.getTopics()
  state.update { currentValue -> UiState(topics) }
}
Clone this wiki locally