Anko Commons Tutorial

See how to improve the readability and conciseness of your Android Kotlin code for Intents, Dialogs and more using the Anko library from JetBrains. By Arturo Mejia.

Leave a rating/review
Download materials
Save for later
Share

Anko is a library for Android devs that want to achieve more while writing less. It simplifies common tasks that are tedious and generate a lot of boilerplate, making your code enjoyable to read, concise and clean. Neat and Tidy, just what the doctor ordered for my Java headaches. :]

The folks at JetBrains, makers of titles like Kotlin (A New Hope) and the IntelliJ Platform (the foundation of Android Studio), have created and maintain Anko.

Anko consists of a number of different components:

  • Anko Commons: helpers for intents, dialogs, logging;
  • Anko Layouts: lets you quickly and programattically create type-safe Android layouts;
  • Anko SQLite: helpers for working with Android SQLite;
  • Anko Coroutines: utilities for using Kotlin coroutins.

In this tutorial, we are going to be focused on Anko Commons. Future tutorials will cover other parts of Anko.

You’ll become an Anko Commons master by updating an existing app that doesn’t use Anko, and then comparing how to do the same things with and without Anko. This will help you be able to make a conscious decision about whether or not to use the library.

Note: This tutorial assumes you have previous experience with developing for Android in Kotlin. If you are unfamiliar with the language, have a look at this tutorial. If you’re just beginning with Android, check out some of our Getting Started and other Android tutorials.

Getting Started

Kanime is the app that we are going to use to showcase Anko’s power. It shows a list of my top 10 animes that you should watch. It’s built without using Anko, but not for long, along the way you’re going to update it.

Meet Kanime. :]

Download the Kanime source code using the download button at the top or bottom of the tutorial. Open the starter project up by starting Android Studio 3.1.2 or later and selecting Open an existing Android Studio project:

Open an existing Android Studio project

Before going on with the tutorial, take a look at the existing starter code.

Kanime Structure

The Kotlin source code packages in the starter project appear as follow:

  • Activities
    • MainActivity.kt
      Here is where you show the top 10 best anime list.
    • AnimeDetailActivity.kt
      When you tap an Anime, this activity opens up. It shows all the info about the selected anime.
    • AboutActivity.kt
      A screen to show information about the app.
  • Adapters
    • AnimeAdapter.kt
      Contains all the code to show each anime item on the list.
  • data
    • AnimeDataSource.kt
      Provides all the anime data.
  • model
    • Anime.kt
      Anime model data class.

Now you have a clear picture of how Kanime is structured. Let’s Rock!

Anko

Anko is a set of helpers functions (Kotlin extension functions) that help you to get something done with the least amount of boilerplate code. It’s subdivided into modules that help you to deal with Layouts, SQLite, and Coroutines. This modularization allows you to pick and choose only what you need.

Note: If you don’t have experience using Kotlin extension functions, then please take a look at the optional Extension Function Fundamentals section. It will help you to understand how Anko works under the hood. If you already have experience with extension functions, then feel free the skip this section.

Extension Function Fundamentals (Optional)

To understand how Anko works, you need to understand Kotlin Extension Functions. They allow you to add a function to an existing class without modifying the class.

For example, say you had a Dog class:

Dog.kt

class Dog(val name: String, val breed: String)

In another Kotlin file you could add a function to Dog without modifying the original file:

Extensions.kt

package com.raywenderlich.doggy

fun Dog.bark(): Unit{
  println("woof woof")
}

To create the extension function, after fun type the class, then a dot, then the name of the extension function.

You could test your extension function in another file as follows”

Main.kt


//Importing bark extension function :]
import com.raywenderlich.doggy.bark

fun main(args: Array<String>) {
  var myPuppy = Dog("Max", "Pug")
  myPuppy.bark()
}

To use the extension function, you only import bark, and then every Dog object will be able to use the bark() function.

Let’s see what happen if you don’t import the bark:

Main.kt


//import com.raywenderlich.doggy.bark
fun main(args: Array<String>) {
  var myPuppy = Dog("Max", "Pug")
  myPuppy.bark() // Compile error ¯\_(ツ)_/¯
}

Note: If you don’t import the extension function, you won’t be able to use it, because it won’t be visible in your code. For this reason, the dog isn’t able to bark. ¯\_(ツ)_/¯

After this small introduction to Kotlin extension functions, now you are ready to become an Anko rock star!

Setting Up Anko Commons

Let’s spice up Kanime by adding Anko Commons!

To set up Anko in your project you only need to include the Gradle dependency. The dependency comes in two flavors:

1.The full Anko artifacts

implementation "org.jetbrains.anko:anko:$anko_version"

This dependency contains all Anko components. If you are only going to use some of them then this is not the way to go, because it will add unnecesary size to your APK for code you are not going to use.

2. Specific Artifacts

Anko allows you to pick and choose only the dependencies that you need:

implementation "org.jetbrains.anko:anko-commons:$anko_version"

This only brings the specific classes for Anko Commons. We are going go with this one for Kanime because it is more lightweight.

Open the app module build.gradle file and add this line to your dependencies :

implementation "org.jetbrains.anko:anko-commons:0.10.5"
Note: As of this writing, the latest version of Anko is 0.10.5. You can find the last version of Anko here. Each new release comes with release notes for what has changed since the last version.

Click “Sync Now” to sync your Gradle files and wait a bit until it finishes.

Hooray!! Now you can use Anko Commons in your project.

Using Anko Commons

Let’s do a deep dive to see what Anko Commons can do for you.

Intents

Intents help you to send data from one Android Component to another. Code to use them can be cumbersome and long. Anko give you a more concise and pleasant way to use intents.

For example, in Kanime you show a list of animes in MainActivity, and when an anime is tapped, it will open AnimeDetailActivity with all the information about the anime.

For this reason, you need an Intent to open AnimeDetailActivity to pass it the information about the selected anime.

Lets see some code!

In the MainActivity.kt file find the function openDetailActivity() that opens the detail screen.

Here is what the code looks like without Anko. :[

private fun openDetailActivity(anime: Anime) {
  // 1 Create the intent and specify the target activity
  val intent = Intent(this, AnimeDetailActivity::class.java)

  // 2 Add anime details
  intent.putExtra("TITLE_KEY", anime.name)
  intent.putExtra("DESCRIPTION_KEY", anime.description)
  intent.putExtra("IMDB_LINK_KEY", anime.imdbLink)
  intent.putExtra("IMAGE_KEY", anime.imageDrawable)
  
  // 3. open the detail activity
  startActivity(intent)
    }

You create an Intent, then have a number of lines to add extras onto the Intent. There are a lot of steps and the end code is not pretty readable.

Replace the function with the equivalent code from Anko Commons:

private fun openDetailActivity(anime: Anime) {
  startActivity<AnimeDetailActivity>(
      "TITLE_KEY" to anime.name,
      "DESCRIPTION_KEY" to anime.description,
      "IMDB_LINK_KEY" to anime.imdbLink,
      "IMAGE_KEY" to anime.imageDrawable
    )
}

You need only one statement to achieve the same end and the code is completely readable!
You may need to hit Option+Return on Mac or Alt+Enter on PC to pull in the Anko extension function import.

To specify the target activity, you put the name of the class after the startActivity as <TargetActivity>. If you want to know more about how this works, read about Generics here.

To specify the additional data, you pass as arguments one or more Kotlin Pair objects to the function startActivity<TargetActivity>(). Each Pair is an extra that you are sending to the activity that you want to call.

Quick Intro to Kotlin Pairs (Optional)

A Pair is a class in Kotlin that helps you to store two values, and is similar to a Tuple in other languages.

If you got goosebumps, when you saw the to operator, don’t worry since it’s a shortcut infix function for creating Pair. The first parameter goes before the to and the second after to. For example:

val myPair  = "TITLE_KEY" to "Berserk"
println("${myPair.first} ${myPair.second}")
// prints TITLE_KEY Berserk

This is the same as if you were to write:

val myPair2  = Pair("TITLE_KEY","Berserk")
println("${myPair2.first} ${myPair2.second}")
// Prints TITLE_KEY Berserk

Now you are a pair master :]

Intent without passing data

When you tap the menu icon, Kanime opens AboutActivity.

In MainActivity, find the function openAboutActivity and replace it with the Anko version:

private fun openAboutActivity() {
  startActivity<AboutActivity>()
}

Here is the code prior to the switch to Anko:

private fun openAboutActivity() {
  val intent = Intent(this, AboutActivity::class.java)
  startActivity(intent)
}

Using Anko, with one line and a lot less code, you are able to open an activity without worrying about creating an Intent object.

Common Intents

Anko Commons offers a great set of “common” intents that are pretty easy to use. Take a look at the table below.

In AnimeDetailActivity, there are a lot of Intents like “share the anime with a friend” the anime and “open the IMDB profile of the anime in a web browser”.

share anime

Let’s see how you can improve AnimeDetailActivity using Anko Commons.

Share Anime

In AnimeDetailActivity, search for the function shareAnime() and update it with the Anko version:

private fun shareAnime() {
  share("$title \n $imdbLink", "Give a checkout to $title")
}

The non-Anko code looked like this:

private fun shareAnime() {
  val text = "$title \n $imdbLink"
  val subject = "Give a checkout to $title"
  
  //1. Creating the Intent
  val intent = Intent(android.content.Intent.ACTION_SEND)
  
  //2. Add extra data
  intent.type = "text/plain"
  intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject)
  intent.putExtra(android.content.Intent.EXTRA_TEXT, text)
  
  //3. open a list of possible apps that can handle this intent
  startActivity(Intent.createChooser(intent, null))
}

Nope, your eyes are not lying to you, this is for real!

With just one line, you are saving your self going off to search Google on the specific constants for the Intent for sharing with all the right parameters.

Open a Web browser

In AnimeDetailActivity, find the function openAnimeInTheBrowser() and update it to the Anko Version:

private fun openAnimeInTheBrowser(){
  browse(imdbLink)
}

Here is the code without Anko:

private fun openAnimeInTheBrowser(){
   val intent = Intent(Intent.ACTION_VIEW)
   intent.data = Uri.parse(imdbLink)
   startActivity(intent)
}

Again, a huge savings in code and improvement in readability!

Send an email

In AnimeDetailActivity, find the function sendAnimeByEmail() and modify it to the cool Anko version:

private fun sendAnimeByEmail(emailAddress: String, subject: String, body: String) {
  email(emailAddress, subject, body)
}

Here is the code without Anko:

private fun sendAnimeByEmail(emailAddress: String, subject: String, body: String) {
  //1. Creating the Intent
  val intent = Intent(Intent.ACTION_SENDTO)

  //2. Add extra data
  intent.data = Uri.parse("mailto:")
  intent.putExtra(Intent.EXTRA_EMAIL, arrayOf(emailAddress))
  intent.putExtra(Intent.EXTRA_SUBJECT, subject)
  intent.putExtra(Intent.EXTRA_TEXT, body)

  //3. Open the email app
  startActivity(intent)
}

You’ve again gone done to just one line of code with Anko!

Toasts, Snackbars and Dialogs

Dialogs are pretty important widgets when you want to communicate with your users, and Anko Commons make your life easier when working with them. It covers most of the more common widgets like Toast and SnackBars, and many flavors of Dialogs.

Toast

In AnimeDetailActivity, find the functions showShortToast and showLongToast and replace them with the Anko versions.

private fun showShortToast(message:String){
  toast(message)
}

private fun showLongToast(message: String) {
  longToast(message)
}

Here is the code without Anko:

private fun showShortToast(message: String) {
  Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

private fun showLongToast(message: String) {
  Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}

You’ve reduced a lot of the boilerplate by switching to Anko.

The best part of using toast() and longToast() is that you don’t have to call .show(), after creating the Toast, a million dollar mistake.

Also, toast() and longToast() offer helpful overloaded versions, accepting string resources and String objects, for example:

 
toast("message")
toast(R.string.message)
     
longToast("message")
longToast(R.string.message)

Alerts

Alerts in Anko Commons are a quick solution to show Alert Dialogs:

alerts

In AnimeDetailActivity, find the function showAlert and update it with the Anko version.

private fun showAlert(messageResource: Int, onYesTapped: () -> Unit, onNoTapped: () -> Unit) {
  alert(messageResource) {
    //1. Creating the alert
    yesButton {
      //2. Handling yes button (This is optional)
      onYesTapped()
    }
    noButton {
      //3. Handling no button (This is optional)
      onNoTapped()
    }
  }.show() //4. Showing the alert
}

alert is a pretty handy function, with which you can specify the title and the message of the alert with String objects or String resources.

Also, within the alert function you can specify handlers for yesButton and noButton. And if you want to have full control over the alert, it has an init function. Here are some examples:

   
alert("message").show()
alert("message", "title").show() //The title is optional
alert(R.string.message, R.string.title).show()

alert("message") {
  yesButton { } //Adds the default android.R.string.yes text to the button
  noButton { } //Adds the default android.R.string.no text to the button
}.show()

alert { //the init function where you can configure the alert as you please
  title = "title"
  message = "message"
            
  //Changing the default title
  positiveButton("Yes") {
    //Do something
  }
  //Changing the default title
  negativeButton("No") {
    //Do something
  }
}.show()

Selectors

A selector is a special type of Dialog that allows you to show a list of items.

When you tap either the thumbs up or thumbs down buttons, it shows a Dialog with a list of options in it.

In AnimeDetailActivity, search for the function showSelector() and update it with a shorter one (Anko :] ).

private fun showSelector(
    title: CharSequence, items: List<CharSequence>, onClick: (DialogInterface, Int) -> Unit) {
  selector(title,items,onClick)
}

Here is the code without Anko:

private fun showSelector(
    title: CharSequence, items: List<CharSequence>, onClick: (DialogInterface, Int) -> Unit) {
  val context = this
  
  //1. Creating the AlertDialog
  val alertBuilder = AlertDialog.Builder(context)
  
  //2. Setting the title
  alertBuilder.setTitle(title)
  
  //3. Setting click handlers for each item of the list
  alertBuilder.setItems(Array(items.size) { itemIndex -> items[itemIndex].toString() }) { dialog, which ->
    onClick(dialog, which)
  }.show()
}

As you can see, the Anko version is again much more condensed, reducing the noise in your code, and it also conveys the intention of the code just by simply reading it, instead of trying to infer the meaning of all the non-Anko lines needed to build the selector.

Progress dialogs

A ProgressDialog allows you to indicate to your users that you’re doing something that is going to take a bit to process.

share anime

In MainActivity, find the function showLoadingDialog() and update it to an Anko version:

private fun showLoadingDialog(message: String, title: String): ProgressDialog {
  val dialog = indeterminateProgressDialog(message, title) {
    //Do any customization to the dialog here
     show()
  }
  return dialog
}

Compare this with the non-Anko version:

private fun showLoadingDialog(message: String, title: String): ProgressDialog {
  val dialog = ProgressDialog(this)
  dialog.setMessage(message)
  dialog.setTitle(title)
  dialog.show()
  return dialog
}

You can create different types of progress dialogs switching between progressDialog and indeterminateProgressDialog.

Also, within the progressDialog you can specify the drawable to display the progress value, for example:

  
progressDialog("message","title").show() //The title is optional

progressDialog("message") {
  //configure the alert as you please
  val drawable = getDrawable(R.drawable.spinner)
  setTitle("title")
  // Set the drawable to be used to display the progress value.
  setProgressDrawable(drawable)
  show()
}

indeterminateProgressDialog("message","title").show()

AnkoLogger

Anko improves significantly on the Android Log class. One of the Anko advantages is that you don’t have to include a TAG name on each call to the logger, since by default it takes the name of the class as a tag.

Another positive aspect of Anko logging is that the name of the functions are nicer and more straightforward. The table below compares the Anko logger and the Android logger:

Let’s see some examples in Kanime.

In order to start using Anko Logger, first you must implement AnkoLogger in your class. In this case, have MainActivity implement AnkoLogger:

class MainActivity : AppCompatActivity(), OnAnimeClickListener, AnkoLogger {

In MainActivity, search for the functions logError() and logWTF() and update them with the Anko versions.

private fun logError() {
  error("Log Error") //Will log E/MainActivity: Log Error
}

private fun logWTF() {
  // What a Terrible Failure
  wtf("Log WTF" ) //  //Will log E/MainActivity: Log WTF
}

If you run into any trouble, make sure the imports for Anko are setup correctly:

import org.jetbrains.anko.AnkoLogger
import org.jetbrains.anko.startActivity
import org.jetbrains.anko.error
import org.jetbrains.anko.wtf

Here are the non-Anko versions for comparison:

private fun logError() {
  val tag = "MainActivity"
  val message = "Log Error"
 	
  if (Log.isLoggable(tag, Log.ERROR))
    Log.e(tag, message) //Will log I/MainActivity: Log Error
 }

private fun logWTF() {
  val tag = "MainActivity"
    
  // What a Terrible Failure
  Log.wtf(tag, message) //Will log I/MainActivity: Log WTF 
}

You’ve again reduced the boilerplate quite a bit.

An alternative to implement AnkoLogger is to have a reference to the AnkoLogger class for example:

   
val logger = AnkoLogger<YourActivity>()
logger.error("This is an error")  //Will Print E/YourActivity: This is an error

Also, you can change the tag name like this:

  
val logger = AnkoLogger("YourTag")
logger.error("This is an error") //Will Print E/YourTag: This is an error

In the same file MainActivity.kt, search for the functions
logInfo(),logVerbose(),logDebug() and logWarn(), and update them with the Anko versions.

private fun logInfo() {
  info("Log Info") //Will log I/MainActivity: Log Info

  info {
    "Log Info" //Will log I/MainActivity: Log Info
  }
}

private fun logVerbose() {
  verbose("Log Verbose") //Will log I/MainActivity: Log Verbose

  verbose {
    "Log Verbose" //Will log I/MainActivity: Log Verbose
  }
}

private fun logDebug() {
  debug("Log Debug") //Will log D/MainActivity: Log Debug

  debug {
    "Log Debug" //Will log D/MainActivity: Log Debug
  }
}

private fun logWarn() {
  warn("Log Warn") //Will log W/MainActivity: Log Warn

  warn {
    "Log Warn" //Will log W/MainActivity: Log Warn
  }
}

You see here that each of the logging functions in AnkoLogger have an alternative version that takes a Kotlin lambda as a parameter.

One important detail about AnkoLogger is that every call to the logger is checking if Log.isLoggable(yourTag, Log.SELECTED_LOG_LEVEL) is true. If not it won’t log the statement. Also, in the lambda versions, the lambda result will not be calculated if the particular log level is false.

Go ahead and build and run the final project with all the concise Anko code. You’ll see the app works just like before, but your app code is much more readablee! :]

Where to Go From Here?

You can download the final project with all the Anko changes using the download button at the top and bottom of the tutorial.

Now that you have become a master of using Anko Commons and added new items to your toolbox, you should check out the other parts of Anko: Layouts, SQLite, and Coroutines.

Remember that Anko is an open source project and you can contribute to it in many ways: proposing new features, reporting bugs or submitting a pull request.

If you have any comments or questions, please comment in the forum discussion below.

I hope this tutorial has been helpful for you. If it was remember to share it with your friends! :]