A.
Appendix A: Kotlin Platforms
Written by Ellen Shapiro
Now that you’ve learned about how to use Kotlin, you may be asking yourself: Where can I apply all of this knowledge?
There are many different platforms that allow you to use Kotlin as a programming language — read on to find out more info about what they are!
Kotlin on the JVM
In this book, we’ve discussed the fact that Kotlin was born to be compiled down to Java Virtual Machine bytecode. Kotlin’s interoperability with Java and compilation to JVM bytecode has been a major factor in its quick adoption.
Anything that runs Java can run Kotlin, and there are very few machines that can’t run Java.
There are a couple of key JVM-powered platforms wherein Kotlin’s adoption is rapidly increasing: Android and server-side.
Android
A huge driver of Kotlin’s adoption has been the ability to work around some of the limitations of Android and its various Integrated Development Environments (IDE).
Android developers had used the IntelliJ IDEA IDE, which you’ve been using in this book to develop in Kotlin, for several years when it wasn’t the officially sanctioned Android IDE because it offered better stability than the official IDE, Eclipse.
Eventually, Google heard the feedback from Android developers that IntelliJ IDEA provided a better and more stable environment. Google then teamed with JetBrains to create the Android Studio IDE, which is based on IntelliJ IDEA. Android Studio is now the primary development environment for most Android developers, and support for Eclipse has been deprecated.
Since Android developers had developed trust in JetBrains as makers of strong development tools like IntelliJ IDEA and Android Studio, they were very open to hearing about JetBrains’ new language — Kotlin — when it was first made public.
In addition, Android developers were often frustrated by the Android APIs being tied to Java 6, which didn’t include native support for language features like lambdas and non-nullable types, both of which were not introduced in Java until Java 8.
When Android developers got ahold of Kotlin, they were able to start using these modern language features without having to worry about what version of Java was supported by the Android OS. This opened up new worlds of possibility.
Jake Wharton, a highly influential Android developer, wrote a widely read white paper in January of 2015 about the reasons Kotlin was a better fit for Android than other languages that run on the JVM like Groovy and Scala.
Wharton’s white paper, along with the work of a large number of other Kotlin-using developers who were writing online posts and giving talks on their findings, showed the larger community of Android developers Kotlin’s possibilities.
Developers realized that they’d be able to leverage the benefits of lambdas, real nullability support and functional programming, and not have to worry about what version of Java was being supported by Android. Nullability support is particularly critical on Android, since the Android Activity and Fragment lifecycles often lead to large amounts of your UI being rebuilt on very short notice. Being able to simply use a ?.let instead of writing endless null checks helps keep code both concise and safe.
At Google I/O in 2017, Google announced first-class language support for Kotlin on Android. This was a huge deal since, prior to this, all uses of Kotlin had been purely relying on Android’s decision to run on the JVM as its source of a long-term compatibility guarantee. Later, at Google I/O 2019, Google announced that Android development was becoming “Kotlin-first,” indicating that many new Android SDKs would be developed first in Kotlin and targeted to Kotlin developers.
A lot of managers were somewhat reluctant to stake the success of their apps on this, particularly if they were not able to fully understand the concept of the bytecode being identical.
By making Kotlin a first-class, officially supported language for Android, Google condoned the existing use of Kotlin and gave all Android developers a green light to start using it in production. Management concerns melted away, and adoption of Kotlin increased among Android developers.
As of API 26 (Oreo) and Android Studio 3.0, Android now supports all features of Java 7 and limited features of Java 8. However, Kotlin has taken its existing lead and pressed ahead with additional features that allow it to remain extremely appealing to Android developers.
In particular, there are two officially maintained Kotlin extension libraries, which can make your code easier to both read and reason about.
Android-specific extensions
The Kotlin Android Extensions library, maintained by JetBrains, offers a number of ways to take advantage of code generation to make working with Android more convenient, safe and concise.
The most widely used feature of this library is automatic view binding, which allows you to bring in view references to your Kotlin code that are created and bound automatically based on the ids you give them in a layout XML file, using Kotlin code generation. In Java, adding boilerplate view binding code was such an excruciating and manual process that multiple libraries popped up to try to make it less painful. In Kotlin, view binding has become part of what the core Kotlin team supports.
In addition, there’s now experimental support for a Kotlin @Parcelize annotation, which allows simple generation of Parcelable support. Parcelable allows custom objects to easily be passed around between Activities and Fragments. Again, previously, this took a great deal of boilerplate code in Java but, with this experimental support for @Parcelize, making any custom object conform to the Parcelable interface is as simple as adding a single annotation.
In early 2018, after the 2017 announcement of official Kotlin support on Android, the team at Google introduced a preview version of its own set of Kotlin extensions, named Android KTX. The project is currently in preview and is specifically designed to make Android APIs easier and more idiomatic to use in Kotlin.
One of the simplest examples given in the KTX documentation is simplifying and Kotlin-ifying the editing of SharedPreferences, which is a lightweight persistence framework on Android.
In both Kotlin and Java, the code looks essentially the same without the KTX extensions:
sharedPreferences.edit()
.putBoolean("key", value)
.putBoolean("another_key", anotherValue)
.apply()
This type of code, which uses a builder pattern to pass an instance along a chain of method calls and then includes some kind of finalizing method, is extremely common in Java. But in Kotlin the code looks a bit out of place and slightly too verbose.
With KTX, you’re able to use lambdas for something that looks a bit more at home in Kotlin and avoids the need to call a finalizing method like apply() at the end of a set of changes:
sharedPreferences.edit {
putBoolean("key", value)
putBoolean("another_key", anotherValue)
}
Thanks in part to tools like the Kotlin Android Extenions and KTX, the Android community is extremely excited about the future of Kotlin as a development language. Another place you can see excitement about Kotlin is in the proliferation of Domain-Specific Languages built on top of Kotlin for Android.
Domain-specific languages
Domain-Specific Languages, or DSLs, take code written in a given language and tailor it in a bespoke fashion to exactly the purpose for which you wish to use it. Kotlin makes creating DSLs fairly easy with its support for functions as parameters. Developers who use Kotlin (especially Android developers) have taken significant advantage of this.
One example of this is that the Gradle organization, creators of the build and dependency management system used by Android Studio, has taken its existing Groovy DSL, which uses a non-type safe JVM language, and ported it to Kotlin.
Applications built using Gradle have a build.gradle file that configure the build and bring in dependencies. The build.gradle files are what makes bringing in support for things like Coroutines a matter of simply writing one line beginning with compile or import.
Gradle build files written in Kotlin don’t look all that different than the ones written in Groovy, since the Gradle team worked to make the API equivalent between Kotlin and Groovy. However, the Kotlin Gradle DSL allows type-safe setup of your project in a way that is not possible using Groovy. You’re able to know even more quickly when something you’ve put in your build.gradle file is not correct.
Another great example of a DSL that helps ensure correctness is the Anko project, which is hosted by the Kotlin GitHub organization.
All of the code in Anko is designed to make working with Android APIs in Kotlin both easier to do right and harder to do wrong. The three main places this occurs are:
- SQLite + Cursors: Prior to the introduction of the Room SQLite abstraction layer at Google I/O in 2017, Android’s built-in interaction with SQLite was an overly verbose API for working with cursors — and generally cursing the existence of cursors. Anko’s take on this made cursors considerably easier to use correctly.
- Coroutines: Since Android developers must spend time shipping long running tasks off to a background thread and then calling back to the main thread, there are some wrappers around Coroutines that make them a bit easier to parse and a bit harder to do incorrectly.
-
Programmatic Layouts: Writing programmatic layouts in Java or even in plain Kotlin is a fairly cumbersome journey through boilerplate code. The Anko DSL greatly simplifies programmatic layouts, also enhances the ability to interact with Anko’s Coroutines syntactic sugar — for example, an
OnClickListenerthat gets fired when a button is clicked can easily fire a coroutine off to a background thread, then update the UI when the background work completes.
DSLs are also really useful for making the intent of code clearer to the reader. Jake Wharton provides another good example of this: the Robot Pattern, a way of thinking about writing UI tests. In the example he gave while introducing this pattern in June 2016 (linked at the end of the Appendix), he talked about working on an app that could send money from one user to another.
He started with some basic UI test code to send a payment to another user:
findViewWithText("4").click()
findViewWithText("2").click()
findViewWithHint("Recipient").setText("foo@bar.com")
findViewWithText("Send").click()
Thread.sleep(1000)
findViewWithText("Success!")
The above code is extremely imperative, has a thread sleep call in it (which is never a good sign) and doesn’t cleanly separate what is being done as steps of the test versus what’s being done to validate that the results are correct.
The Robot pattern version of the code looks like this:
payment {
amount(4200)
recipient("foo@bar.com")
}.send() {
isSuccessful()
}
By taking advantage of basic Kotlin language features like lambdas and apply to form a DSL for acceptance testing, the Robot pattern makes several major improvements to the code :
- It’s now type-safe, so you can’t accidentally type a letter into the amount section.
- Setup vs. validation sections are now clearly defined.
- All Espresso (an Android UI testing framework) code is abstracted away - which also means that, in the future, if there’s another UI testing method you want to try, you can do so without changing the actual code in the tests.
Building DSLs in Kotlin isn’t limited to Android — but Android is where creating DSLs has taken off like a rocket.
But there’s another place where Kotlin is starting to make inroads that has primarily been JVM and Java-based for the last several years, and that’s in server-side development.
Kotlin on the server
While adoption of Kotlin on the server is not presently as wide as it is on Android, Kotlin is starting to make more and more moves towards wider server-side acceptance.
Kotlin has started to take off particularly within the community around the Spring framework, which had been a Java-based framework for backend development. Spring introduced official Kotlin support in version 5.0 in early 2017.
JetBrains has also created their very own server-side project, Ktor, and uses it to power their own website. The team notes that its license system has been “written in 100% Kotlin and has been running in production since 2015 with no major issues.”
You can deploy Kotlin web apps on the popular hosting framework Heroku, and also use it with Docker, a popular containerization framework.
Another web-side use of Kotlin is with AWS Lambda, which allows on-demand running of individual functions at a price significantly lower than spinning up a full server. You can write those individual functions in Kotlin, and they’ll run just like they’re running on your local machine.
These options are all great if you want to run Kotlin on the JVM — but what if you just need it to work with some Javascript? Good news! The Kotlin team has got you covered.
Kotlin to JavaScript
Kotlin can be cross-compiled (or “transpiled”) into Javascript, making it far easier to write type-safe web application code without having to give up some of the flexibility of JavaScript.
Kotlin’s cross-compilation can take advantage of two different techniques for interacting with JavaScript:
- CommonJS, which allows you to use Kotlin for server-side JavaScript frameworks like Node.js.
- Asynchronous Module Definition, or AMD, which allows you to use Kotlin for client-side JavaScript.
Both of these techniques take advantage of a kotlin.js file, which brings as much functionality as possible from the Kotlin language over to JavaScript.
One thing to watch out for whenever you’re working with Kotlin for Javascript is that types don’t always line up exactly.
For example, Kotlin’s Long represents a 64-bit integer, which doesn’t exist in JavaScript, and is only supported in the actual runtime library generated by Kotlin.
Speaking of cross-compiled code, there has been a fascinating experimental development in the world of Kotlin recently. This development allows you to run code written in Kotlin on iOS devices among many other options: Kotlin/Native.
Kotlin/Native and Multiplatform
As you read about in Chapter 25, Kotlin/Native is a project from the Kotlin team that uses the LLVM compiler to create code that doesn’t need a virtual machine (such as the JVM) to run. This means that code written in Kotlin could be able to run even faster when it’s executing machine instructions for a specific OS and processor combination than it would by running against the JVM, since it avoids the extra step of having to be executed by a virtual machine. Running natively is also particularly helpful for code you want to run on iOS or macOS, since LLVM is the same compiler used for Objective-C and Swift on those platforms.
In Chapter 26, you read that Kotlin/Native can be used as part of building Kotlin Multiplatform apps. The iOS and Android applications for the KotlinConf 2017 and 2018 conferences were written entirely using the Kotlin Multiplatform approach. If you’re an iOS developer learning Kotlin, it’s a very odd thing to see a whole bunch of UIKit code written in Kotlin, but it’s incredibly impressive that they were able to get a working app out of it.
At the time of writing, the platforms supported for natively compiled code are:
- Windows (x86_64 only)
- Linux (x86_64, arm32, MIPS, MIPS little endian)
- macOS (x86_64)
- iOS (arm64 only)
- Android (arm32 and arm64)
- WebAssembly (wasm32 only)
If you’re working on a cross-platform project, it may be worth looking at what pieces of business logic can be centralized in a reusable fashion using Kotlin/Native. This would be particularly helpful if you don’t have a server-side component, since often logic that doesn’t depend on a given platform is centralized at the server level.
There are a few pitfalls to Kotlin/Native that are common to any “write-once-run-everywhere” solution — and there are a few questions you should regularly ask yourself:
-
Would it be faster, more stable, more efficient or simply easier to work in a platform’s intended language?
-
Am I going to be able to get usable stack traces from crashes?
-
How hard will it be to tell if the problems I run into are caused by the language I’m using, a first-party framework I’m using with a language it’s not intended to be used with, or my own code?
Sometimes, the answers are clear-cut, and the compromises you need to make to use a cross-platform solution are worth it. Sometimes, you’ll realize that you’re bending over backwards to make your project work in Kotlin when you don’t really need to do so. Make sure to regularly ask yourself these questions if you decide to go down the rabbit hole with Kotlin/Native.
Where to go from here?
Here’s what we covered in this chapter:
- Jake Wharton’s 2015 Kotlin white paper: https://docs.google.com/document/d/1ReS3ep-hjxWA8kZi0YqDbEhCqTt29hG8P44aA9W0DM8/edit
- Android Kotlin Extensions from JetBrains: https://kotlinlang.org/docs/tutorials/android-plugin.html
- Android KTX from Google: https://github.com/android/android-ktx/
- Gradle Kotlin DSL: https://github.com/gradle/kotlin-dsl
- Anko: https://github.com/Kotlin/anko
- UI Testing Robots: https://academy.realm.io/posts/kau-jake-wharton-testing-robots
- Spring Framework support for Kotlin: https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0
- ktor Kotlin Server-Side Framework: https://github.com/ktorio/ktor
- Kotlin to JavaScript transpiling documentation: https://kotlinlang.org/docs/tutorials/javascript/kotlin-to-javascript/kotlin-to-javascript.html
- Kotlin/Native documentation: https://kotlinlang.org/docs/reference/native-overview.html
- Kotlin/Native sample app: https://github.com/JetBrains/kotlin-native/tree/master/samples
Now go out and build some cool stuff using Kotlin!