· 5 min read
API request in Android the easy way using Kotlin
Kotlin is a really powerful language aimed to write more code using less boilerplate. And this is specially true in Android. Apart from the language itself and its own classes, Kotlin also provides a good set of useful extensions for already existing Java classes. An example of this is the way to make a request to an API and download the result.
I know that a lot of different libraries already exist to help us do this task, and Kotlin can make use of them because of its interoperability with Java, but we sometimes use big libraries to commit small requirements only because it’s much simpler and less prone to errors.
API Request: Java vs Kotlin
I always like to compare both languages to see what we are missing by sticking to Java. The typical code to recover a json from an url is this:
try {
URL url = new URL("<api call>");
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null) {
// Nothing to do.
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
return null;
}
result = buffer.toString();
} catch (IOException e) {
Log.e("Request", "Error ", e);
return null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e("Request", "Error closing stream", e);
}
}
}
Kotlin standard library provides an extension function for URL
class that prevents us from having to write all that code. Previous code can be converted to:
val result = URL("<api call>").readText()
This function is not recommended for huge responses, but it will be enough in most situations. If it is not, there are many other interesting extension functions such as BufferedReader.forEachLine()
, which creates a Sequence
of the lines and let us do something with any of them. Or you can get the raw Sequence
by using BufferedReader.lineSequence()
. From this moment, you could perform any of the different transformations a Sequence
allows, such as filtering, sorting, mapping, etc.
Asynchronous call
As you know, the main thread is in charge of UI rendering and interaction, and we shouldn’t block it with long tasks, because the UI performance will be affected. In the case of HTTP requests, even the Android SDK will prevent us from doing it by throwing an exception. The typical solution in Android is the use of AsyncTask
. An AsyncTask
has an abstract method called doInBackground
, which is executed in a secondary thread.
Apart from the fact that it’s really difficult to make an AsyncTask
work properly, because of the many problems it brings with itself, it’s really tedious to create a new class which extends from it, cancel it in onDestroy
, etc. A very simple version of this (you’d probably need more checks to avoid crashes) would be:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
task = new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... params) {
return requestFromServer("<api call>");
}
@Override
protected void onPostExecute(String s) {
if (!isFinishing() && !isCancelled()) {
Log.d("Request", s);
Toast.makeText(ExampleActivity.this, "Request performed", Toast.LENGTH_LONG).show();
}
}
};
}
@Override
protected void onDestroy() {
super.onDestroy();
if (task != null) {
task.cancel(true);
task = null;
}
}
Not really clean nor intuitive. When we are working with Kotlin, a library we can’t miss is Anko. It’s main goal is to provide a DSL to create layouts using code instead of XML. I’m really used to XML, so at the moment I don’t use it for that, but it includes a whole set of extremely useful features. In particular, there’s a small DSL for asynchronous tasks. The previous code could be reduced to this in Kotlin:
async {
val result = URL("<api call>").readText()
uiThread {
Log.d("Request", result)
longToast("Request performed")
}
}
Basically you have an async
function that will execute its code in another thread, and will give the chance of returning main thread using uiThread
. async
is an extension function implemented for Context
, and will use a weak reference of it, so it won’t prevent GC from releasing its memory.
The good part about uiThread
is that it’s implemented differently depending on the class it uses. If we call it from an Activity
, the uiThread
code won’t be executed if activity.isFinishing()
returns true
, and it won’t crash in that situation.
async
returns a java Future
, in case you want to work with futures. And if you need it to return a future with a result, you can use asyncResult
.
You also can use your own executor:
val executor = Executors.newScheduledThreadPool(4)
async(executor) {
// Some task
}
Conclusion
With a few lines of code, we’re getting the same (if not better) result from a very typical operation such as making an API call and get its result in a String variable. There’s a lot of interesting code behind these extension functions, so I recommend you to review the source code of Kotlin and Anko, and see what’s doing behind the scenes.
And remember you can learn this and many other things about Kotlin in the book I’m writing: Kotlin for Android Developers, where you will learn Kotlin by creating an Android App from the ground up.