Kotlin : call API

Ressources : https://friendlyuser.github.io/posts/tech/kotlin/Parsing_JSON_with_Klaxon_in_Kotlin/

Code du projet : https://app.box.com/s/gd6vv0xjmfaw6pl1q15up0ngninrhvnz

Thank’s to : https://medium.com/@chris_42047/making-rest-calls-in-kotlin-android-using-okhttp-and-couroutines-dcff3b525ad6

Here a quick Kickstart Tutorial for making REST calls with OkHttp and Kotlin Coroutines in your Android app.

This tutorial will provide step-by-step instructions on:

  1. Configuring your app for making REST calls
  2. Using OkHttp to make a Get call
  3. Launch a Get request from a background thread
  4. Use ViewModel and LiveData to update the UI.

Please ‘follow me’ on Medium if you like this post.

1. Versions used

  • Android Studio 4.1.1
  • Android API 29
  • OkHttp 4.9.0
  • view model and livedata 2.2.0
  • klaxon 5.0.1

2. Creating a project

First, let’s create a new project using the ‘Basic Activity’ template. In Android Studio, select the ‘File’ menu, ‘New Project’ menu item.

Select ‘Next’.

On the Project Configuration modal, give the project a name (e.g. RestSample1), select a project root directory and select minimum API level 21.

Select ‘Finish’ to create the project.

3. Enabling Internet permission

In order to be able to make REST calls, Android requires to add the INTERNET permission to the AndroidManifest.xml.

The permission is granted by adding the ‘uses-permission’ tag outside of the application block.<! — ?xml version= »1.0″ encoding= »utf-8″? →
<manifest xmlns:android= »http://schemas.android.com/apk/res/android &raquo; package= »com.example.restsample1″><uses-permission android:name= »android.permission.INTERNET »><application>

</application></uses-permission>
</manifest>

Since the internet permission is a common permission, you don’t have to go through the permission request flow that is required for other types of permissions, e.g. accessing the user location.

4. Configuring OkHttp

In this blog we are using the OkHttp library from Square to launch the Http request. It’s an excellent, mature library with Apache 2.0 license that has been used by many apps in production (39K stars on GitHub).

OkHttp is a great option because it implements a number of performance enhancements out of the box:

  • HTTP result caching
  • HTTP connection pooling
  • Transparent GZipping of results to shrink download size
  • HTTP/2 socket sharing.

OkHttp works on Android 5.0+ (API level 21+) and on Java 8+.

The complete documentation for OKHttp is here.

There are other options for launcht HTTP requests, e.g. Android SDK HttpURLConnection, FastHttp, etc. To understand how to use HttpURLConnection, check out my blog post.

In order to use the HttpOk library, we have to first install the dependency. In your app build gradle, add the following line under ‘dependencies’. Then press the ‘Sync’ button on the top right corner.implementation « com.squareup.okhttp3:okhttp:4.9.0 »

On a high level, to use OkHttp, we have to:

  1. Create a shared OkHttp client
  2. Convert the URL string to an URL object
  3. Build a OkHttp request using the url
  4. Execute the request by passing the request to the client
  5. Reading the response body.

MainActivity.kt// Create OkHttp Client
var client: OkHttpClient = OkHttpClient();private fun getRequest(sUrl: String): String? {
var result: String? = nulltry {
// Create URL
val url = URL(sUrl) // Build request
val request = Request.Builder().url(url).build() // Execute request
val response = client.newCall(request).execute()
result = response.body?.string()
}
catch(err:Error) {
print(« Error when executing get request: « +err.localizedMessage)
} return result
}

4. Starting REST calls from a background thread

To prevent locking up the app UI by making a blocking network calls, Android doesn’t allow making REST calls on the main thread so instead we want to initiate them from a background thread.

There are several options for this type of implementation with various levels of complexity (e.g. AsyncTask, Service, JobScheduler, WorkManager or even a Java Thread).

For simplicity, some developers may select AsyncTask for this type of implementation. However, AsyncTask often causes context leaks, missed callbacks, or crashes on configuration changes. AsyncTast was deprecated in API level 30 (see Android documentation).

Instead, in this tutorial we are going to use Kotlin coroutines, which also provides async functionality and is a lightweight solution with little risk for memory leaks. For more information check out the documentation for Kotlin coroutines.

In order to be able to use Kotlin coroutines, we need to add the corresponding dependency to the app build gradle:implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9’

Add the dependency in the app build gradle dependency section and then select ‘Sync now’ on the top right to sync your project with the gradle settings.

Here are the important elements of Kotlin coroutines:

Suspend: The suspend keyword marks a Kotlin function to wait until the result has returned. You may be familiar with async/await in Java or JavaScript. Suspend works the same way where the execution inside the function may be asynchronous and running on a separate thread but the code after the suspended function does not continue until the asynchronous code completes.

Dispatcher: The Dispatcher let’s you specify which thread is used to execute the coroutine. Below are the dispatchers that are available:

Launch without parameters: In case you execute GlobalScope.launch without anyparameters, it inherits the context (and thus dispatcher) from the CoroutineScope it is being launched from. In this case, it inherits the context of the main runBlocking coroutine which runs in the main thread.

Dispatchers.default: The default dispatcher that is used when coroutines are launched in GlobalScope is represented by Dispatchers.Default and uses a shared background pool of threads.

newSingleThreadContext: creates a thread for the coroutine to run.

Dispatchers.Unconfined: The Dispatchers.Unconfined coroutine dispatcher starts a coroutine in the caller thread, but only until the first suspension point. After suspension it resumes the coroutine in the thread that is fully determined by the suspending function that was invoked. The unconfined dispatcher is appropriate for coroutines which neither consume CPU time nor update any shared data (like UI) confined to a specific thread.

You can read more about Dispatchers in the Kotlin documentation.

Coroutine Scope: The scope of a coroutines specifies the lifetime for which the coroutine runs.

There are three default scopes provided for Kotlin coroutines:

  • Global Scope
  • LifeCycle Scope
  • ViewModel Scope

Global Scope: When Coroutine is launched within Global scope, it stays alive as long as the application does. Once the app dies, the coroutines is terminated as well. It is not recommended to launch Coroutines in Global scope, unless there’s a good reason for it. Using GlobalScope let’s you specify the dispatcher (thread) that should be used.

LifeCycle Scope: When Coroutine is launched within LifeCycle scope, it stays alive as long as the Activity from which it was started is alive. LifeCycle Scope does not let you specify the dispatcher to be used.

ViewModel Scope: When Coroutine is launched within ViewModelscope, it stays alive as long as the ViewModel from which it is launched, is alive.

In this tutorial we are just going to use the lifecycle scope with IO Dispatcher. This will launch out network request on a separate thread.

In order to update the UI from within the coroutine, you can use withContext(Dispatchers.Main) {} to use the main thread again.

First, create a data class to hold in the information coming back from the HTTP request. Create a new Kotlin class ‘BlogInfo’ and create the data class:data class BlogInfo(val title: String, val description: String)

Next, create a private function in the MainActivity to create a coroutine and launch the get request:private fun fetch(sUrl: String): BlogInfo? {
var blogInfo: BlogInfo? = null
lifecycleScope.launch(Dispatchers.IO) {
val result = getRequest(sUrl)
}
return blogInfo
}

5. Parsing the Response

The standard REST response format for mobile apps is JSON (versus XML) due to it’s compact format. There are several options to parse the JSON string format returned from the REST call and convert it into a Kotlin class that you can work with.

In the future, the kotlinx serialization library will probably the default means to parse JSON. However, currently it is still in the experimental stage. You can read more about kotlinx serialization here. I would encourage you to check the release status and see if it has changed.

For this blog I decided to use the Klaxon JSON parser, which is an open source library with Apache 2.0 license. Here the documentation.

In order to use it, add Klaxon as a dependency in your build gradle and sync the project:implementation ‘com.beust:klaxon:5.0.1’

Next, you can start parse the JSON string response into the BlogInfo data class we created using the Klaxon().parse() function:import com.beust.klaxon.Klaxonprivate fun fetch(sUrl: String): BlogInfo? {
var blogInfo: BlogInfo? = null
lifecycleScope.launch(Dispatchers.IO) {
val result = getRequest(sUrl)
if (result != null) {
try {
// Parse result string JSON to data class
blogInfo = Klaxon().parse<bloginfo>(result)
}
catch(err:Error) {
print(« Error when parsing JSON: « +err.localizedMessage)
}
}
else {
print(“Error: Get request returned no response”)
}
}
return blogInfo
}

6. Updating the UI

Now that we processed the API response, we can update the information in the UI. The recommended way to do this is using the Android Jetpack architectural components of ViewModel and LiveData. The advantage of using these components is that they persist through Activity lifecycle changes so for example when the device orientation changes and the Activity is recreated, ViewModel and LiveData will persist and the information is reused in the created activity.

In order to use ViewModel and LiveData, add the following dependencies to your app build gradle and sync the project:// Enable live data and view model
implementation « androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0 »
implementation « androidx.lifecycle:lifecycle-livedata-ktx:2.2.0 »
implementation « androidx.activity:activity-ktx:1.1.0 »

Next, create a Kotlin class called MainActivityViewModel and add the following:import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModelclass MainActivityViewModel : ViewModel() {
val title = MutableLiveData<string>()
val description = MutableLiveData<string>()
}

Now we are ready to use the View Model in our MainActivity. As I mentioned before, UI updates will need to be done on the main thread so we are going to use withContext(Dispatchers.Main) {}.// View model
import androidx.lifecycle.Observer
import androidx.activity.viewModelsprivate fun fetch(sUrl: String): BlogInfo? {
var blogInfo: BlogInfo? = null
lifecycleScope.launch(Dispatchers.IO) {
val result = getRequest(sUrl)
if (result != null) {
try {
// Parse result string JSON to data class
blogInfo = Klaxon().parse<bloginfo>(result) withContext(Dispatchers.Main) {
// Update view model
viewModel.title.value = blogInfo?.title
viewModel.description.value = blogInfo?.description
}
}
catch(err:Error) {
print(« Error when parsing JSON: « +err.localizedMessage)
}
}
else {
print(« Error: Get request returned no response »)
}
}
return blogInfo
}

Let’s create UI fields to display the information in our MainActivity layout. Replace the content in your content_main.xml with the following:<! — ?xml version= »1.0″ encoding= »utf-8″? →
<androidx.constraintlayout.widget.constraintlayout xmlns:android= »http://schemas.android.com/apk/res/android &raquo; xmlns:app= »http://schemas.android.com/apk/res-auto &raquo; android:layout_width= »match_parent » android:layout_height= »match_parent » android:padding= »24dp » app:layout_behavior= »@string/appbar_scrolling_view_behavior »><textview android:id= »@+id/textview_blog_title » android:layout_width= »wrap_content » android:layout_height= »wrap_content » android:layout_margintop= »24dp » android:text= » » app:layout_constraintstart_tostartof= »parent » app:layout_constrainttop_totopof= »parent »><textview android:id= »@+id/textview_blog_description » android:layout_width= »wrap_content » android:layout_height= »wrap_content » android:text= » » app:layout_constraintstart_tostartof= »parent » app:layout_constrainttop_tobottomof= »@id/textview_blog_title »><button android:id= »@+id/button_fetch » android:layout_width= »wrap_content » android:layout_height= »wrap_content » android:text= »@string/fetch » android:layout_margintop= »24dp » app:layout_constraintstart_tostartof= »parent » app:layout_constraintend_toendof= »parent » app:layout_constrainttop_tobottomof= »@id/textview_blog_description »>

In your MainActivity, register the UI components. Here we are going to:

  1. Create variables for the text and button fields
  2. Link the variables up to the layout
  3. Create a click listener for the fetch button to execute a request
  4. Register observers for the title and description view model live data to update our UI components.

class MainActivity : AppCompatActivity() {
var textViewTitle: TextView? = null
var textViewDescription: TextView? = null
var buttonFetch: Button? = null
val viewModel: MainActivityViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
…textViewTitle = findViewById(R.id.textview_blog_title)
textViewDescription = findViewById(R.id.textview_blog_description)
buttonFetch = findViewById(R.id.button_fetch)buttonFetch?.setOnClickListener(View.OnClickListener {
// Launch get request
fetch(« https://raw.githubusercontent.com/justmobiledev/android-kotlin-rest-1/main/support/data/bloginfo.json &raquo;)
})viewModel.title.observe(this, Observer {
textViewTitle?.text = it
})viewModel.description.observe(this, Observer {
textViewDescription?.text = it
})
}
}

As an alternative to creating observers, you can also directly bind the view model to the layout using Android data binding.

And that’s it. When you run the project now, you should see a ‘Fetch’ button. When the button is clicked, the Get request is executed and the UI is updated with the result using ViewModel and LiveData.

You can also try to click the fetch button and rotating the device. You will see that the UI fields are still updated with the response info.

The complete sample project can be found on GitHub here.

Articles récents
Commentaires récents
fatima dans Bienvenue !
AdminDroid dans Bienvenue !
fatima dans Bienvenue !
Archives
Catégories