Step #4: Retrieving the Items
Now, we can add some code that will download the JSON and convert it into a list of ToDoServerItem
objects.
Right-click over the com.commonsware.todo.repo
package in the java/
directory and choose “New” > “Kotlin File/Class” from the context menu. For the name, fill in ToDoRemoteDataSource
, and choose “Class” for the kind. Press Enter or Return to create the class. Then, replace the class contents with:
package com.commonsware.todo.repo
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import java.io.IOException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
class ToDoRemoteDataSource(private val ok: OkHttpClient) {
private val moshi = Moshi.Builder().add(MoshiInstantAdapter()).build()
private val adapter: JsonAdapter<List<ToDoServerItem>> = moshi.adapter(
Types.newParameterizedType(
List::class.java,
ToDoServerItem::class.java
)
)
suspend fun load(url: String) = withContext(Dispatchers.IO) {
val response = ok.newCall(Request.Builder().url(url).build()).execute()
if (response.isSuccessful) {
response.body?.let { adapter.fromJson(it.source()) }
?: throw IOException("No response body: $response")
} else {
throw IOException("Unexpected HTTP response code: ${response.code}")
}
}
}
This class has a load()
function that orchestrates our work for downloading and parsing the JSON. This will involve network I/O, so load()
is defined as a suspend
function and it uses withContext(Dispatchers.IO)
to use the coroutine system to run this code on a background thread.
First, we need to download the JSON, and for that, we use OkHttp. Our constructor gets an OkHttpClient
, which is our entry point for using OkHttp. load()
gets the URL of the JSON as a parameter. We then:
- Wrap that URL in an OkHttp
Request
object (Request.Builder().url(url).build()
) - Tell OkHttp to create a
Call
object representing our request (newCall()
) - Execute the HTTP request on the current thread (
execute()
)
This will make the connection to the server and try to download the JSON. We get a Response
object back which (hopefully) contains our JSON along with other bits of information from the Web service, such as an HTTP response code (e.g., 200 for an “OK” response). We check to see if the Response
is successful by checking the isSuccessful
property. If it was not successful, we throw an exception indicating the nature of the problem (e.g., our URL is wrong and we got a 404 response from the server).
We then check to see if the response has a body (body()
) — this represents the JSON itself. If, for some reason, we do not have a body, we throw an exception to indicate that fact.
Finally, if we have a successful response and it has a body, we need to try to parse the JSON. For that, we use Moshi. Our ToDoRemoteDataSource
has a moshi
property which is a Moshi
object, created using Moshi.Builder
. In our case, we teach Moshi how to handle Calendar
properties by adding our MoshiInstantAdapter()
to the Moshi
instance.
By and large, Moshi is a series of adapter classes. Some we write ourselves, such as MoshiInstantAdapter()
. Some are code-generated for us at compile time, such as the adapter for ToDoServerItem
that we requested via the @JsonClass(generateAdapter = true)
annotation that we placed on the ToDoServerItem
class. And some are code-generated for us at runtime, such as the adapter
property that we have in ToDoRemoteDataSource
. That builds a JsonAdapter
that knows how to parse JSON into a List
of ToDoServerItem
objects. In load()
, we pass our JSON (source()
called on the response.body()
object) to this JsonAdapter
, and it will return our List
of ToDoRemoteDataSource
objects… or will throw an exception is there is some parsing problem.
Kotlin does not use Java-style checked exceptions, but it is obvious that we have multiple possible exceptions coming from load()
. Given that we are attempting to download data from the Internet, there are lots of ways that this can go wrong, all of which will lead to exceptions.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.