How to fetch data over network in Kotlin and Android

Reading string and JSON over network

Networking is an essential component of apps. Most of the apps we use are connected to the internet and involve reading/writing data over the internet. In this tutorial, we will learn how to perform network requests in Kotlin. Although you can also use a third-party library such as Retrofit, Volley and such, understanding how it's done in Kotlin is worthwhile. So let's get started!

Making a network request in Kotlin is straightforward with simple syntax. Here's how you would read data over the internet in Kotlin:

val response = URL("https://httpbin.org/get").readText()

However, of course, if you try it on the main thread, you will get the NetworkOnMainThreadException exception. To get away with this, we need to make the network call in the background. One way to do this is by using an Async task. An Async task was a pain to implement in Java, but we can do it quite easily using Anko (a library for Kotlin). This is how you would create a background task in Kotlin using Anko:

doAsync{
    val result = URL("https://httpbin.org/get").readText()
    uiThread {
        toast(result)
    }
}

The uiThread method is provided by Anko library, which you can include in your project by adding these lines in your build.gradle:

implementation "org.jetbrains.anko:anko:0.10.4"

In Java's Async task implementation, the Async task could be fired even if the activity was being destroyed. This resulted in defensive programming where you had to make checks on whether the UI was still present to do UI operations. However, Anko's implementation of background task takes care of it, and it won't fire the task if the activity is dying.

The doAsync returns a Java Future. In simple words, a Future is a proxy or a wrapper around an object that is not yet there. When the asynchronous operation is done, you can extract it. If you want to avoid working with Future, doAsync has a different construct that accepts an ExecutorService:

val executor = Executors.newScheduledThreadPool(5)

doAsync(executorService = executor){
    val result = URL("https://httpbin.org/get").readText()
    uiThread {
        toast(result)
    }
}

The uiThread block isn't executed if the activity is closing. The reason is that it doesn't hold a context instance, only a weak reference. So even if the block isn't finished, the context will not leak.

We are going to update the AndroidManifest.xml file by adding the following user permissions

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

How to download a file in Kotlin

We often need to download files in our Android application. The most basic way of doing this will be opening a URL connection and using InputStream to read the content of the file and storing it in a local file using FileOutputStream; all this is in a background thread using AsyncTask. However, we don’t want to reinvent the wheel. There are a lot of libraries out there that handle all this stuff very nicely for us and make our work super easy, helping us create clean code.

For this recipe, we will be using a networking library called Fuel, which is written in Kotlin. Add Fuel dependencies to your project dependencies by adding the following lines in your build.gradle and syncing the project:

compile 'com.github.kittinunf.fuel:fuel:1.12.1'

Let's start with a button in our view with an onClickListener attached to it. I am also adding a progressBar to the view to be able to see the progress of our download. This is my view:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <Button
        android:id="@+id/btnDownload"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="32dp"
        android:layout_marginTop="32dp"
        android:text="Download"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:progress="0"/>
</android.support.constraint.ConstraintLayout>

Let's start by downloading a temporary file. We will be using https://httpbin.org/ for mocking download file API. The following is the code for downloading a temporary file:

class DownloadFileActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_download_file)

        btnDownload.onClick {
            progressBar.progress = 0
            Fuel.download("http://httpbin.org/bytes/32768").destination { response, url ->
                File.createTempFile("boo", ".tmp")
            }.progress { readBytes, totalBytes ->
                val progress = readBytes.toFloat() / totalBytes.toFloat()
                progressBar.progress = progress.toInt()*100
            }.response { req, res, result ->
                Log.d("status result", result.component1().toString())
                Log.d("status res", res.responseMessage)
                Log.d("status req", req.url.toString())
            }
        }
    }
}