Kotlin logo

Kotlin

A concise multiplatform language developed by JetBrains

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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
val intent = Intent(this, javaClass<SomeActivity>())
intent.putExtra("id", 5)
intent.putExtra("name", "John")
startActivity(intent)
val intent = Intent(this, javaClass<SomeActivity>()) intent.putExtra("id", 5) intent.putExtra("name", "John") startActivity(intent)
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
startActivity<SomeActivity>("id" to 5, "name" to "John")
startActivity<SomeActivity>("id" to 5, "name" to "John")
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.

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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
browse("http://somewebsite.org (http://somewebsite.org/)")
email("admin@domain.net (mailto:admin@domain.net)", "Here I am!", "Message text")
browse("http://somewebsite.org (http://somewebsite.org/)") email("admin@domain.net (mailto:admin@domain.net)", "Here I am!", "Message text")
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
alert("Order", "Do you want to order this item?") {
positiveButton("Yes") { processAnOrder() }
negativeButton("No") { }
}.show()
alert("Order", "Do you want to order this item?") { positiveButton("Yes") { processAnOrder() } negativeButton("No") { } }.show()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
val flowers = listOf("Chrysanthemum", "Rose", "Hyacinth")
selector("What is your favorite flower?", flowers) { i ->
toast("So your favorite flower is ${flowers[i]}, right?")
}
val flowers = listOf("Chrysanthemum", "Rose", "Hyacinth") selector("What is your favorite flower?", flowers) { i -> toast("So your favorite flower is ${flowers[i]}, right?") }
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
progressDialog("Please wait a minute.", "Downloading…")
indeterminateProgressDialog("Fetching the data…")
progressDialog("Please wait a minute.", "Downloading…") indeterminateProgressDialog("Fetching the data…")
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
alert {
customView {
verticalLayout {
val familyName = editText {
hint = "Family name"
}
val firstName = editText {
hint = "First name"
}
positiveButton("Register") { register(familyName.text, firstName.text) }
}
}
}.show()
alert { customView { verticalLayout { val familyName = editText { hint = "Family name" } val firstName = editText { hint = "First name" } positiveButton("Register") { register(familyName.text, firstName.text) } } } }.show()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
if (!wifiManager.isWifiEnabled()) {
vibrator.vibrate(200)
toast("Wifi is disabled. Please turn on!")
}
if (!wifiManager.isWifiEnabled()) { vibrator.vibrate(200) toast("Wifi is disabled. Please turn on!") }
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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
async(someExecutor) { // omit the parameter to use the default executor
// This code will be executed in background
}
async(someExecutor) { // omit the parameter to use the default executor // This code will be executed in background }
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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
async {
// Do some work
uiThread {
toast("The work is done!")
}
}
async { // Do some work uiThread { toast("The work is done!") } }
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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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. */
}
}
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. */ } }
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class SomeActivity : Activity(), AnkoLogger {
fun someMethod() {
info("Info message")
debug(42) // .toString() method will be called automatically
}
}
class SomeActivity : Activity(), AnkoLogger { fun someMethod() { info("Info message") debug(42) // .toString() method will be called automatically } }
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).

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
info("String " + "concatenation")
info { "String " + "concatenation" }
info("String " + "concatenation") info { "String " + "concatenation" }
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