Introduction to Kotlin: Android Programming For Humans
tejas
In a perfect Android world, the main language of Java is really modern, clear, and elegant. You can write less by doing more, and whenever a new feature appears, developers can use it just by increasing version in Gradle. Then while creating a very nice app, it appears fully testable, extensible, and maintainable. Our activities are not too large and complicated, we can change data sources from database to web without tons of differences, and so on. Sounds great, right? Unfortunately, the Android world isn’t this ideal. Google is still striving for perfection, but we all know that ideal worlds don’t exist. Thus, we have to help ourselves in that great journey in the Android world.
What Is Kotlin, and Why Should You Use It?
So, the first language. I think that Java isn’t the master of elegance or clarity, and it is neither modern nor expressive (and I’m guessing you agree). The disadvantage is that below Android N, we are still limited to Java 6 (including some small parts of Java 7). Developers can also attach RetroLambda to use lambda expressions in their code, which is very useful while using RxJava. Above Android N, we can use some of Java 8’s new functionalities, but it’s still that old, heavy Java. Very often I hear Android developers say “I wish Android supported a nicer language, like iOS does with Swift”. And what if I told you that you can use a very nice, simple language, with null safety, lambdas, and many other nice new features? Welcome to Kotlin.
What is Kotlin?
Kotlin is a new language (sometimes referred to as Swift for Android), developed by the JetBrains team, and is now in its 1.0.2 version. What makes it useful in Android development is that it compiles to JVM bytecode, and can be also compiled to JavaScript. It is fully compatible with Java, and Kotlin code can be simply converted to Java code and vice versa (there is a plugin from JetBrains). That means Kotlin can use any framework, library etc. written in Java. On Android, it integrates by Gradle. If you have an existing Android app and you want to implement a new feature in Kotlin without rewriting the whole app, just start writing in Kotlin, and it will work.
But what are the ‘great new features’? Let me list a few:
Optional and Named Function Parameters
fun createDate(day: Int, month: Int, year: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) {
print("TEST", "$day-$month-$year $hour:$minute:$second")
}
If a variable can be null, code will not compile unless we force them to make it. The following code will have an error – nullableVar may be null:
var nullableVar: String? = “”;
nullableVar.length;
To compile, we have to check if it’s not null:
if(nullableVar){
nullableVar.length
}
Or, shorter:
nullableVar?.length
This way, if nullableVar is null, nothing happens. Otherwise, we can mark variable as not nullable, without a question mark after type:
var nonNullableVar: String = “”;
nonNullableVar.length;
This code compiles, and if we want to assign null to nonNullableVar, compiler will show an error.
There is also very useful Elvis operator:
var stringLength = nullableVar?.length ?: 0
Then, when nullableVar is null (so nullableVar?.length returns null), stringLength will have value 0.
Mutable and Immutable Variables
In the example above, I use var when defining a variable. This is mutable, we can reassign it whenever we want. If we want that variable to be immutable (in many cases we should), we use val (as value, not variable):
val immutable: Int = 1
After that, the compiler will not allow us to reassign to immutable.
Lambdas
We all know what a lambda is, so here I will just show how we can use it in Kotlin:
Extensions are a very helpful language feature, thanks to which we can “extend” existing classes, even when they are final or we don’t have access to their source code.
For example, to get a string value from edit text, instead of writing every time editText.text.toString() we can write the function:
fun EditText.textValue(): String{
return text.toString()
}
Or shorter:
fun EditText.textValue() = text.toString()
And now, with every instance of EditText:
editText.textValue()
Or, we can add a property returning the same:
var EditText.textValue: String
get() = text.toString()
set(v) {setText(v)}
Operator Overloading
Sometimes useful if we want to add, multiply, or compare objects. Kotlin allows overloading of binary operators (plus, minus, plusAssign, range, etc.), array operators ( get, set, get range, set range), and equals and unary operations (+a, -a, etc.)
Data Class
How many lines of code do you need to implement a User class in Java with three properties: copy, equals, hashCode, and toString? In Kaotlin you need only one line:
data class User(val name: String, val surname: String, val age: Int)
This data class provides equals(), hashCode() and copy() methods, and also toString(), which prints User as:
User(name=John, surname=Doe, age=23)
Data classes also provide some other useful functions and properties, which you can see in Kotlin documentation.
Anko Extensions
You use Butterknife or Android extensions, don’t you? What if you don’t need to even use this library, and after declaring views in XML just use it from code by its ID (like with XAML in C#):
There are many other useful things in Anko, including starting activities, showing toasts, and so on. This is not the main goal of Anko – it is designed for easily creating layouts from code. So if you need to create a layout programmatically, this is the best way.
This is only a short view of Kotlin. I recommend reading Antonio Leiva’s blog and his book – Kotlin for Android Developers, and of course the official Kotlin site.
What Is MVP and Why?
A nice, powerful, and clear language is not enough. It’s very easy to write messy apps with every language without good architecture. Android developers (mostly ones who are getting started, but also more advanced ones) often give Activity responsibility for everything around them. Activity (or Fragment, or other view) downloads data, sends to save, presents them, responds to user interactions, edits data, manages all child views . . . and often much more. It’s too much for such unstable objects like Activities or Fragments (it’s enough to rotate the screen and Activity says ‘Goodbye….’).
A very good idea is to isolate responsibilities from views and make them as stupid as possible. Views (Activities, Fragments, custom views, or whatever presents data on screen) should be only responsible for managing their subviews. Views should have presenters, who will communicate with model, and tell them what they should do. This, in short, is the Model-View-Presenter pattern (for me, it should be named Model-Presenter-View to show connections between layers).
“Hey, I know something like that, and it’s called MVC!” – didn’t you think? No, MVP is not the same as MVC. In the MVC pattern, your view can communicate with model. While using MVP, you don’t allow any communication between these two layers – the only way View can communicate with Model is through Presenter. The only thing that View knows about Model can be the data structure. View knows how to, for example, display User, but doesn’t know when. Here’s a simple example:
View knows “I’m Activity, I have two EditTexts and one Button. When somebody clicks the button, I should tell it to my presenter, and pass him EditTexts’ values. And that’s all, I can sleep until next click or presenter tells me what to do.”
Presenter knows that somewhere is a View, and he knows what operations this View can perform. He also knows that when he receives two strings, he should create User from these two strings and send data to model to save, and if save is successful, tell the view ‘Show success info’.
Model just knows where data is, where they should be saved, and what operations should be performed on the data.
Applications written in MVP are easy to test, maintain, and reuse. A pure presenter should know nothing about the Android platform. It should be pure Java (or Kotlin, in our case) class. Thanks to this we can reuse our presenter in other projects. We can also easily write unit tests, testing separately Model, View and Presenter.
MVP pattern leads to better, less complex codebase by keeping user interface and business logic truly separate.
A little digression: MVP should be a part of Uncle Bob’s Clean Architecture to make applications even more flexible and nicely architectured. I’ll try to write about that next time.
Sample App with MVP and Kotlin
That’s enough theory, let’s see some code! Okay, let’s try to create a simple app. The main goal for this app is to create user. First screen will have two EditTexts (Name and Surname) and one Button (Save). After entering name and surname and clicking ‘Save’, the app should show ‘User is saved’ and go to the next screen, where saved name and surname is presented. When name or surname is empty, the app should not save user and show an error indicating what’s wrong.
The first thing after creating Android Studio project is to configure Kotlin. You should install Kotlin plugin, and, after restart, in Tools > Kotlin you can click ‘Configure Kotlin in Project’. IDE will add Kotlin dependencies to Gradle. If you have any existing code, you can easily convert it to Kotlin by (Ctrl+Shift+Alt+K or Code > Convert Java file to Kotlin). If something is wrong and the project does not compile, or Gradle does not see Kotlin, you can check code of the app available on GitHub.
Kotlin not only interoperates well with Java frameworks and libraries, it allows you to continue using most of the same tools that you are already familiar with.
Now that we have a project, let’s start by creating our first view – CreateUserView. This view should have the functionalities mentioned earlier, so we can write an interface for that:
interface CreateUserView : View {
fun showEmptyNameError() /* show error when name is empty */
fun showEmptySurnameError() /* show error when surname is empty */
fun showUserSaved() /* show user saved info */
fun showUserDetails(user: User) /* show user details */
}
As you can see, Kotlin is similar to Java in declaring functions. All of those are functions that return nothing, and the last have one parameter. This is the difference, parameter type comes after name. The View interface is not from Android – it’s our simple, empty interface:
interface View
Basic Presenter’s interface should have a property of View type, and at least on method (onDestroy for example), where this property will be set to null:
interface Presenter<T : View> {
var view: T?
fun onDestroy(){
view = null
}
}
Here you can see another Kotlin feature – you can declare properties in interfaces, and also implement methods there.
Our CreateUserView needs to communicate with CreateUserPresenter. The only additional function that this Presenter needs is saveUser with two string arguments:
We also need Model definition – it’s mentioned earlier data class:
data class User(val name: String, val surname: String)
After declaring all interfaces, we can start implementing.
CreateUserPresenter will be implemented in CreateUserPresenterImpl:
class CreateUserPresenterImpl(override var view: CreateUserView?): CreateUserPresenter<CreateUserView> {
override fun saveUser(name: String, surname: String) {
}
}
The first line, with class definition:
CreateUserPresenterImpl(override var view: CreateUserView?)
Is a constructor, we use it for assigning view property, defined in interface.
MainActivity, which is our CreateUserView implementation, needs a reference to CreateUserPresenter:
class MainActivity : AppCompatActivity(), CreateUserView {
private val presenter: CreateUserPresenter<CreateUserView> by lazy {
CreateUserPresenterImpl(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
saveUserBtn.setOnClickListener{
presenter.saveUser(userName.textValue(), userSurname.textValue()) /*use of textValue() extension, mentioned earlier */
}
}
override fun showEmptyNameError() {
userName.error = getString(R.string.name_empty_error) /* it's equal to userName.setError() - Kotlin allows us to use property */
}
override fun showEmptySurnameError() {
userSurname.error = getString(R.string.surname_empty_error)
}
override fun showUserSaved() {
toast(R.string.user_saved) /* anko extension - equal to Toast.makeText(this, R.string.user_saved, Toast.LENGTH_LONG) */
}
override fun showUserDetails(user: User) {
}
override fun onDestroy() {
presenter.onDestroy()
}
}
At the beginning of the class, we defined our presenter:
private val presenter: CreateUserPresenter<CreateUserView> by lazy {
CreateUserPresenterImpl(this)
}
It is defined as immutable (val), and is created by lazy delegate, which will be assigned the first time it is needed. Moreover, we are sure that it will not be null (no question mark after definition).
When the User clicks on the Save button, View sends information to Presenter with EditTexts values. When that happens, User should be saved, so we have to implement saveUser method in Presenter (and some of the Model’s functions):
override fun saveUser(name: String, surname: String) {
val user = User(name, surname)
when(UserValidator.validateUser(user)){
UserError.EMPTY_NAME -> view?.showEmptyNameError()
UserError.EMPTY_SURNAME -> view?.showEmptySurnameError()
UserError.NO_ERROR -> {
UserStore.saveUser(user)
view?.showUserSaved()
view?.showUserDetails(user)
}
}
}
When a User is created, it is sent to UserValidator to check for validity. Then, according to the result of validation, proper method is called. The when() {} construct is same as switch/case in Java. But it is more powerful – Kotlin allows use of not only enum or int in ‘case’, but also ranges, strings or object types. It must contain all possibilities or have an else expression. Here, it covers all UserError values.
By using view?.showEmptyNameError() (with a question mark after view), we are protected from NullPointer. View can be nulled in onDestroy method, and with this construction, nothing will happen.
When a User model has no errors, it tells UserStore to save it, and then instruct View to show success and show details.
As mentioned earlier, we have to implement some model things:
enum class UserError {
EMPTY_NAME,
EMPTY_SURNAME,
NO_ERROR
}
object UserStore {
fun saveUser(user: User){
//Save user somewhere: Database, SharedPreferences, send to web...
}
}
object UserValidator {
fun validateUser(user: User): UserError {
with(user){
if(name.isNullOrEmpty()) return UserError.EMPTY_NAME
if(surname.isNullOrEmpty()) return UserError.EMPTY_SURNAME
}
return UserError.NO_ERROR
}
}
The most interesting thing here is UserValidator. By using the object word, we can create a singleton class, with no worries about threads, private constructors and so on.
Next thing – in validateUser(user) method, there is with(user) {} expression. Code within such block is executed in context of object, passed in with name and surname are User’s properties.
There is also another little thing. All above code, from enum to UserValidator, definition is placed in one file (definition of User class is also here). Kotlin does not force you to have each public class in single file (or name class exactly as file). Thus, if you have some short pieces of related code (data classes, extensions, functions, constants – Kotlin doesn’t require class for function or constant), you can place it in one single file instead of spreading through all files in project.
When a user is saved, our app should display that. We need another View – it can be any Android view, custom View, Fragment or Activity. I chose Activity.
So, let’s define UserDetailsView interface. It can show user, but it should also show an error when user is not present:
interface UserDetailsView {
fun showUserDetails(user: User)
fun showNoUserError()
}
Next, UserDetailsPresenter. It should have a user property:
interface UserDetailsPresenter<T: View>: Presenter<T> {
var user: User?
}
This interface will be implemented in UserDetailsPresenterImpl. It has to override user property. Every time this property is assigned, user should be refreshed on the view. We can use a property setter for this:
class UserDetailsPresenterImpl(override var view: UserDetailsView?): UserDetailsPresenter<UserDetailsView> {
override var user: User? = null
set(value) {
field = value
if(field != null){
view?.showUserDetails(field!!)
} else {
view?.showNoUserError()
}
}
}
UserDetailsView implementation, UserDetailsActivity, is very simple. Just like before, we have a presenter object created by lazy loading. User to display should be passed via intent. There is one little problem with this for now, and we will solve it in a moment. When we have user from intent, View needs to assign it to their presenter. After that, user will be refreshed on the screen, or, if it is null, the error will appear (and activity will finish – but presenter doesn’t know about that):
class UserDetailsActivity: AppCompatActivity(), UserDetailsView {
private val presenter: UserDetailsPresenter<UserDetailsView> by lazy {
UserDetailsPresenterImpl(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user_details)
val user = intent.getParcelableExtra<User>(USER_KEY)
presenter.user = user
}
override fun showUserDetails(user: User) {
userFullName.text = "${user.name} ${user.surname}"
}
override fun showNoUserError() {
toast(R.string.no_user_error)
finish()
}
override fun onDestroy() {
presenter.onDestroy()
}
}
Passing objects via intents requires that this object implements Parcelable interface. This is very ‘dirty’ work. Personally, I hate doing this because of all of the CREATORs, properties, saving, restoring, and so on. Fortunately, there is proper plugin, Parcelable for Kotlin. After installing it, we can generate Parcelable just with one click.
The last thing to do is to implement showUserDetails(user: User) in our MainActivity:
override fun showUserDetails(user: User) {
startActivity<UserDetailsActivity>(USER_KEY to user) /* anko extension - starts UserDetailsActivity and pass user as USER_KEY in intent */
}
And that’s all.
We have a simple app that saves a User (actually, it is not saved, but we can add this functionality without touching presenter or view) and presents it on the screen. In the future, if we want to change the way user is presented on the screen, such as from two activities to two fragments in one activity, or two custom views, changes will be only in View classes. Of course, if we don’t change functionality or model’s structure. Presenter, who doesn’t know what exactly View is, won’t need any changes.
In our app, Presenter is created every time an activity is created. This approach, or its opposite, if Presenter should persist across activity instances, is a subject of much discussion across the Internet. For me, it depends on the app, its needs, and the developer. Sometimes it’s better is to destroy presenter, sometimes not. If you decide to persist one, a very interesting technique is to use LoaderManager for that.
As mentioned before, MVP should be a part of Uncle Bob’s Clean architecture. Moreover, good developers should use Dagger to inject presenters dependencies to activities. It also helps maintain, test, and reuse code in future. Currently, Kotlin works very well with Dagger (before the official release it wasn’t so easy), and also with other helpful Android libraries.
Wrap Up
For me, Kotlin is a great language. It’s modern, clear, and expressive while still being developed by great people. And we can use any new release on any Android device and version. Whatever makes me angry at Java, Kotlin improves.
Of course, as I said nothing is ideal. Kotlin also have some disadvantages. The newest gradle plugin versions (mainly from alpha or beta) don’t work well with this language. Many people complain that build time is a bit longer than pure Java, and apks have some additional MB’s. But Android Studio and Gradle are still improving, and phones have more and more space for apps. That’s why I believe Kotlin can be a very nice language for every Android developer. Just give it a try, and share in the comments section below what you think.
Source code of the sample app is available on Github: github.com/tomaszczura/AndroidMVPKotlin This post originally appeared in Toptal