Android Multiplatform

Advanced Features of Anko

Last week we published a new version of Anko. While the main purpose of this library is creating layouts though a DSL, even the users of XML layouts can benefit from it. Today we are going to talk about such “ambivalent” features of Anko.

Intent Helpers

The common way of starting a new Activity is to create an Intent, maybe put some parameters into it, and finally pass the created Intent to the startActivity() method of a Context.

val intent = Intent(this, javaClass<SomeActivity>())
intent.putExtra("id", 5)
intent.putExtra("name", "John")
startActivity(intent)

With Anko we can do this in exactly one line of code:

startActivity<SomeActivity>("id" to 5, "name" to "John")

startActivity() function accepts key-value pairs that will be passed as Intent extra parameters. Another function, startActivityForResult() with similar semantics is also available.

Please refer to the Intent Builder Functions reference section for more information.

Popular Intent Shorthands

Almost every application has code that loads a page in the default web browser or opens a new email screen using Android intents, so there are helper functions for this in Anko:

browse("http://somewebsite.org (http://somewebsite.org/)")
email("admin@domain.net (mailto:admin@domain.net)", "Here I am!", "Message text")

Other useful intents are described under the Useful Intent Callers reference section.

Alert Dialogs

Anko provides a declarative way of creating alert dialogs with text messages, lists, progress bars and even with your own DSL layout.

For a simple text alert with a couple of buttons at the bottom, all you need is:

alert("Order", "Do you want to order this item?") {
    positiveButton("Yes") { processAnOrder() }
    negativeButton("No") { }
}.show()

There is a function that creates and shows a list dialog:

val flowers = listOf("Chrysanthemum", "Rose", "Hyacinth")
selector("What is your favorite flower?", flowers) { i ->
    toast("So your favorite flower is ${flowers[i]}, right?")
}

Both indeterminate and basic progress dialogs are supported:

progressDialog("Please wait a minute.", "Downloading…")
indeterminateProgressDialog("Fetching the data…")

Also, as mentioned above, you can use Anko’s DSL in dialogs to create a custom layout:

alert {
    customView {
        verticalLayout {
            val familyName = editText {
                hint = "Family name"
            }
            val firstName = editText {
                hint = "First name"
             }
             positiveButton("Register") { register(familyName.text, firstName.text) }
         }
    }
}.show()

Services

Android system services, such as WifiManager, LocationManager or Vibrator, are available in Anko through extension properties for the Context:

if (!wifiManager.isWifiEnabled()) {
    vibrator.vibrate(200)
    toast("Wifi is disabled. Please turn on!")
}

Asynchronous Tasks

Probably the most popular way to execute code in the background is to subclass an AsyncTask. But, despite of its popularity, it is inconvenient in many ways. Anko has several functions which practically do the same but are easier to use.

async() {...} function executes code inside {} under the ThreadExecutor. You can use the default one or pass your own.

async(someExecutor) { // omit the parameter to use the default executor
// This code will be executed in background
}

If you want to go back to the UI thread inside async(), you can use uiThread() function.

async {
    // Do some work
    uiThread {
        toast("The work is done!")
    }
}

uiThread() has a special semantics inside async(): async() does not hold a Context instance but only a WeakReference to it, so even if lambda execution never finishes, the Context instance will not leak.

async {
    uiThread {
        /* Safe version. This code won't be executed
            if the underlying Context is gone. */
    }
    ctx.uiThread {
    /* Here we are calling the `uiThread`
        extension function for Context directly,
        so we are holding a reference to it. */
    }
}

Logging

Android SDK provides the android.util.Log class which consists of a few logging methods. Usage is straightforward but the methods require passing a tag argument, and the actual log message must be a String. You can get rid of this by using the AnkoLogger trait:

class SomeActivity : Activity(), AnkoLogger {
    fun someMethod() {
        info("Info message")
        debug(42) // .toString() method will be called automatically
    }
}

The default tag name is a class name (SomeActivity in this case), but you can easily change it by overriding the loggerTag property of AnkoLogger.

Each method has two versions: plain and “lazy” (lambda will be executed only if Log.isLoggable(tag, Log.INFO) is true).

info("String " + "concatenation")
info { "String " + "concatenation" }

You can read more about logging in the Logging reference section.

Conclusion

To try Anko, follow these instructions.

And as usual, your feedback is very welcome.

image description