Kotlin
A concise multiplatform language developed by JetBrains
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.