It might be overwhelming to choose the best way to access a web service from your Android app. Maybe all you want is to parse JSON from a web service and show it in a list in your Kotlin app for Android, while still being future-proof with a library like Retrofit. As a bonus, it’d be great if you could also perform CRUD operations (create, read, update, delete) with the data.
You can choose from basic Java-style HTML requests, or go up to full-scale MVVM design patterns with the new Android Architecture Components. Your source code will look entirely different depending on what approach you chose – so it’s important to make a good choice right at the beginning.
In this article, I’ll show a walk-through using many of the newest components for a modern solution:
- Retrofit v2: web service access
- Moshi: converting JSON to Kotlin data classes and back
- Kotlin Coroutine Adapter for Retrofit: handles threading for asynchronous web access
- Object declaration: for singleton patterns in Kotlin
- RecyclerView with a click adapter: showing items in a list
Updated on December 15th, 2020: the solution projects on GitHub have been migrated to the latest versions and dependencies. Most importantly, the new solutions now also use Jetpack View Bindings instead of Kotlin synthetics. The text in this article is still the original.
Updated on July 4th, 2019: Google is transitioning the additional libraries to AndroidX. Nothing changes in terms of behavior with regards to our example. I’ve updated the source code examples on GitHub to use AndroidX instead of the Android Support libraries.
Sample Scenario: Starter Project & Web Service
We’re building on the previous article where we created a list with a RecyclerView and then added a click listener. You can download the starter project.
The sample scenario is an item management software for an imaginary factory. But it’s generic. You can easily adapt the code to your needs – no matter if you want to create a to-do list, load weather data or a highscore list from a web service.
Local Demo Web Service
The easiest way to test our app is a flexible local mock web server. Once you have your Android code in place, you’ll just switch the live target URL. But testing with a local server is a lot easier, as you have full control over both sides.
An excellent way to create a local web service is the JSON Server project by typicode. You’ll have a fully working mock restful web server in a few minutes. First, make sure you have Node.js installed.
Next, create a starter JSON file, which the server uses as database. Call it db.json and store it to an empty directory.
Now, open this directory with the command line. Type the following to install the json-server module into a shared location through the npm package manager. It might help if you open the powershell window with administrator rights.
npm install -g json-server
Finally, simply start the server. As parameter, specify the JSON file you just created. This will serve as database and define the URLs for the CRUD operations of the restful server.
json-server --watch db.json
When you open the URL stated in the terminal, you’ll see the JSON returned by the server. Note that in the screenshot below it’s parsed and made prettier by Firefox; but it’s of course exactly the same file as our database file we provided. However, the server even allows adding, updating and deleting items through standard REST calls. The db.json fill will always be updated accordingly.
By default, your web server will run for the localhost address – which is fine if you use the emulator to access the server. To access it from a mobile phone in the same local network, launch json-server using your computer’s IP address. First, check your address using ipconfig from a Powershell window. For example, your computer’s local IP could be 10.2.1.205. Then, you’d launch the server with:
json-server --watch db.json --host 10.2.1.205
You can try accessing your server from your phone through its web browser and your computer’s IP. The port stays the same (by default 3000).
Web Access for Android: Retrofit
Android allows many different options for accessing web services. The plain Java variant is easy to understand, but by far not powerful and flexible enough for a web service. In the world of Android, two libraries are commonly used:
- Volley by Google: you’d expect it to be the “official” networking library of Android. On GitHub, it has been starred around 2,000 times. Apache 2.0 license.
- Retrofit by Square: with the same Apache 2.0 license, it gained 31,000 stars on GitHub.
Both have some differences in how they work, and both are good choices. You’ll find a lot of heated discussions about which library is better.
From my experience, Retrofit seems to be more widely used in general. The main reason why I chose Retrofit for this tutorial: Google is also using it in the sample code for their newest Architecture Components.
Prepare your App: Dependencies
Let’s get our app ready to use Retrofit. First, add the Kotlin-Kapt plugin at the end of your plugin list from build.gradle of your app module. Kapt is an annotation pre-processor. It’ll allow us to add annotations to our Kotlin data class to help Moshi with converting the code to JSON and vice versa:
apply plugin: 'kotlin-kapt'
Next, add the required dependencies to build.gradle of your app module. We’ll talk about all the additional dependencies besides retrofit later.
Converting Between JSON and Kotlin Classes: Moshi
For native apps, you’ll always want an array of data objects in the end to easily show it in the UI and to interact with the contents. JavaScript directly converts JSON to classes. But for native code, we’d like more control. The exact structure of the class should be pre-defined in our app, so that during conversion from JSON everything can be checked and is type-safe.
The difficult part is the mapping between JSON and our own class. For example, in some cases you’d like the property to be called differently than the item’s name in JSON. This is where converters come in.
Retrofit comes with many ready-made converters. Two are most prominent:
- Gson by Google: has been around since 2008.
- Moshi by Square, the developers of Retrofit: created in 2014.
One of the main developers of Moshi apparently first created Gson, but since then left Google and felt that he’d like to create a new converter that addresses some of the issues with Gson at a very low level that essentially requires a rewrite. The result: Moshi.
Moshi has great Kotlin support as well as compile-time code generation to make apps faster and smaller. You could – but you don’t need to add a large generic library to your app. So let’s give Moshi a try. One of the lines in the dependencies section that we added before triggers the code generation during compilation. Here it is again for reference:
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.8.0"
Annotate the Kotlin Data Class for Moshi
How to instruct Moshi to automatically generate an adapter for our data class? Just add a single annotation before the class declaration. Here is the complete data class to store an item id and an item name.
It’d be possible to add further annotation, e.g., to give the properties different names than their JSON counterparts. But for simplicity, we’ll stick with the same names; so, don’t need any further mappings.
That’s all you need to map from JSON to Kotlin. When you compile the app, Moshi will add an additional, auto-generated adapter class that takes care of everything for you.
API Client Interface & Call Adapters
Mapping the JSON isn’t enough for accessing a web service. We also need a nice and easy way to map the server’s interface to Kotlin functions.
This part sits right at the connection between the web service and the rest of your app. As such, it’s heavily influenced by how you handle the web request’s asynchronous nature. As usual, you have multiple options.
An often used library is called RxJava 2. Retrofit includes a few ready-made adapters for RxJava and others. Essentially, the aim is always to make asynchronous calls easier than in standard Java.
Kotlin Coroutines
We’re writing our app in Kotlin. While RxJava is of course compatible, Kotlin recently added an exciting new feature: coroutines. It makes asynchronous programming a native language feature – with a syntax that is a bit like how C# handles async calls. In my opinion, the Kotlin coroutines have more flexibility, but loose some of C#’s async/await elegance.
Coroutines are an excellent feature which will make your life easier. I won’t go into too much detail here; we’ll just use them. With coroutines, your asynchronous code looks almost the same as synchronous code. You don’t need to write tedious callbacks anymore. You can read more about coroutines in the Kotlin documentation. Google also provides a long Coroutine Code Lab.
In the earlier section of this article, we already included the coroutines extensions for Kotlin dependency. Jake Warthon, one of the most prominent Android developers, also created a Retrofit Call Adapter for Kotlin Coroutines. It’s still at version 0.9.2, but I expect this approach to be the future for using asynchronous code in Kotlin.
API Client Interface with Kotlin Coroutines for Retrofit
In many cases, you only need HTTP GET operations. However, in this article I’d like to show you all four possible CRUD operations that are possible with web services:
Add a new file to the project, this time of type interface. Let’s analyze the four lines of code.
Each line defines a different operation: GET, POST, DELETE and PUT. Each of these is provided as a normal Kotlin function.
For normal GET requests to retrieve data from a web service, we use the @GET annotation in front of the function definition. The parameter of the annotation denotes the path of the web service. In this case, it means that the GET request should be mapped to: http://127.0.0.1/parts. When that URL is called, the service expects to get a JSON with all the data Moshi needs to transform it to a list of instances of our PartData class.
Deferred Response as Function Return Variable
To analyze the complex return value of the function, we go from inside out:
Deferred<Response<List<PartData>>>
Obviously, we want Moshi to parse the JSON and return a List of PartData instances. That’s easy.
The list is wrapped in a Response class. This is from Retrofit and provides full access to the server’s HTTP response. In most cases, this is important to have as well; after all, you need to know if the request was successful.
GET usually returns JSON data in its response body. Other functions like DELETE often don’t contain response body data to parse; so we need to look at the HTTP response header to see if the request was successful.
The outside class is Deferred. This is coming from the Kotlin Coroutines. It defines a job that has a result. Essentially, it’s the magic that makes our application wait for the web server’s results, without blocking the rest of the application.
POST, DELETE and PUT requests
The code for the other three CRUD operations are comparable, with some minor details changed.
@POST("parts") fun addPartAsync(@Body newPart : PartData): Deferred<Response<Void>>
POST (adds a new item) additionally needs a request body: the complete JSON of the new item which we send to the web server. As such, the function needs a parameter where we can send the JSON. Again, Moshi takes care of the conversion; so we simply work with the Kotlin classes. The @Body annotation makes sure this data ends up in the body of the HTTP request. Our test server doesn’t return body data in its response; so the function return value is Void.
@DELETE("parts/{id}") fun deletePartAsync(@Path("id") id: Long) : Deferred<Response<Void>> @PUT("parts/{id}") fun updatePartAsync(@Path("id") id: Long, @Body newPart: PartData) : Deferred<Response<Void>>
DELETE and PUT have another specialty: they require the ID of the object to delete / modify in the HTTP URL. It’s marked in the path definition. An additional @Path annotation tells the library which parameter should be used for the path.
- DELETE: the resulting request URL should be: http://127.0.0.1/parts/123456, with DELETE as HTTP method.
- PUT (modifies an existing item): http://127.0.0.1/parts/123456, with PUT as HTTP method to change the object, and the JSON of the new data as request body.
Singleton in Kotlin for Retrofit
Our project should only have a single instance of the Retrofit HTTP client for a particular URL. This ensures that Retrofit properly manages its connections to the web server. As such, it’s a bad idea to tie the Retrofit client directly to an Activity. Especially with Android’s lifecycle, the class would be re-created every time you rotate the display. A better approach is the new LiveData component, which is livecycle-aware.
As our Retrofit instance isn’t really a data holder that LiveData was made for, it’s probably better to use the Singleton pattern for creating a single instance of Retrofit for the whole app when it’s used for the first time. This will also enable us to access the web service from multiple activities.
Add another new Kotlin File/Class to your project and choose the type “Object”. To create a Singleton in Java, you’d need to write the corresponding code yourself. It’s easy to make mistakes if you take multi-threading into account. Because of that, Kotlin includes native support for Singleton-like code. Instead of using the “class” keyword, you define it with “object”.
In this class, we only need one property: an instance of the API client. By adding the keywords “by lazy” after the variable type definition, we tell Kotlin that it should execute the following lambda code only the first time a class tries to access the partsApi variable. Afterwards, it will just return the created instance. We don’t need to write any code for it. Plus, it’s thread-safe!
I also added a log message in the code above, so that you can check and see when exactly this code gets executed while your app is running.
Building Retrofit
The main code of this lambda contains a big chain of function calls from the Retrofit builder.
First, we add the base URL of the web service. For now, we’ll test the app using the Google Android emulator. As such, within the emulator, 127.0.0.1 points to the emulator itself. However, we want to access the web service that’s running in the OS outside of the emulator. By default, the magic number where the emulator maps your computer’s localhost to the emulator is 10.0.2.2. As you remember when creating our JSON-server, it’s running at port 3000.
Converter & Call Adapter
Next, we tell Retrofit which Converter and Call Adapter to use. We’ve already included both as dependencies into our app. Moshi is our JSON to Kotlin converter. The Coroutine call adapter should take care of managing the asynchronous flow.
In the last line of the lambda, we let Retrofit create itself, based on the mapping interface to our web service. This completes the singleton creation of Retrofit with Kotlin!
Retrofit GET Request with Kotlin Coroutines
The only remaining task is to trigger the asynchronous web request. Let’s start with the GET request to retrieve the list of items from the web service.
For this, we use Kotlin Coroutines. One of the best introductory articles I’ve found about how Coroutines work was written by Joffrey Bion.
We used the suspend functionality when we set up the interface through the Deferred type. This means that the function will suspend until the result is available. The rest of our app’s code can continue to run in the meantime and the app will stay responsive.
You are allowed to call one suspending function from within another suspending function. But at some point, you need to “bridge” to the normal world. Our UI interface listener was not set up with a suspend keyword; as such, it can not suspend in the middle of the function.
Coroutine Builder
The solution is a coroutine builder. It creates a new coroutine and launches it from a normal function. You only need to be aware of the context: who does the coroutine belong to? Should it be tied to a parent, should it run in a separate thread or in Android’s UI thread?
Coroutines must have a scope where they’re attached to. Using the activity itself is problematic: rotating the screen would pull the scope away under a running asynchronous task, due to the re-created activity.
Scopes & Livecycle
The easiest solution is to use the GlobalScope. This means that the task can always continue, even if our activity gets destroyed. This could also be a problem if there’s an error in the task and it gets orphaned. The Kotlin documentation contains an example of how to make sure the job gets cancelled if the activity is destroyed. A more specific example for Android was posted on StackOverflow by Marko Topolnik.
As such, a slightly better solution would be to use the ViewModel from the Android Architecture Components. However, as ViewModels require a more significant change in our code, the GlobalScope is OK for simple web requests like in our case, and to get started with coroutines.
Coroutine Context and Dispatchers
So, let’s launch the coroutine from a function. First, we use a coroutine builder. In this case, launch launches a new coroutine without blocking the current thread. It returns a reference to a Job, which would allow us to cancel the running coroutine. We don’t use that here.
As parameter, we specify the dispatcher. Dispatchers.Main is specific to the Android Coroutines extensions. It runs our code on the UI thread. This allows us to update the UI from within the coroutine.
With await added to the call of getPartsAsync(), we suspend execution of the lambda until the webResponse results are in. We don’t need to write a callback for this anymore! Our code is short and concise.
Note that we could switch to the IO context for blocking network operations for this call. This would ensure the networking code doesn’t get executed on the UI thread. However, it seems that the underlying libraries already take care of that. Otherwise, Android wouldn’t allow us to execute the network call at all. So, we should be fine keeping our own code on the Main dispatcher.
Next, we check if the web request was successful. If it was, we take the list of items and assign it to the recycler view adapter. As we used Moshi, it already performed the mapping of the JSON response to a list of class instances for us.
IOException for Network Errors
With the code above, your app would handle errors returned by the web server. However, it’d still still crash for more fundamental errors. Examples: your web server isn’t running, or the user has no active data connection.
These kinds of errors are thrown with an IOException. Surround the actual web service call with try/catch to inform the user about the issue. The improved code of the function:
Add, Update and Delete Operations
Adding the other three CRUD operations is similar. You just need to ensure you provide the correct parameters of the interface we specified. Here are some simple functions to trigger these operations:
Closing Thoughts & More Information
While you need to be aware of a lot of concepts, the actual amount of code to elegantly access a web service is minimal. Consider what you gain: a completely salable process that works for any web service. And you could potentially endlessly load items thanks to the efficiency of the RecyclerView.
You can download the finished example code from GitHub. Note that it’s configured to run in the emulator with the local test server created at the beginning of the article. To run it with a real-life server, update the IP address in WebAccess.kt.
As mentioned at the beginning, there are many alternatives of how you could implement this scenario. Another good example was posted by Okta , which uses RxJava and Gson instead of Kotlin Coroutines and Moshi. Of course, you can also go with the new Android Architecture Components and use ViewModels and LiveData to access the web service via RetroFit. But that’s a different story 🙂