Writing on Kotlin for Android

    About two years ago, I discovered that in the Android Market there are no applications convenient for me to track the time spent on different projects. Like a real idler, I decided not to search for what was required, but to write it myself. It turned out to be a little more complicated than it seemed at the beginning, and I abandoned everything in a sluggish hobby mode, the embodiment of my desire lived for more than a year, slowly moving from the idea to the alpha version.

    And then suddenly kind people asked me to tell how to write for Android on Kotlin'e. It seemed to me that this is a good sign, and I quickly rewrote the application on Kotlin, finished it and put it on Google Play.

    Important clarification: this article is only about how to write for Android on Kotlin'e. It will be interesting only to those who want to try Kotlin, and those who want to understand why to try it. The application I wrote is very, very simple (and by itself does not apply to JetBrains in any way). There is a guarantee that something can be improved in his code; it is posted on Github for an example only, and not as a sample of amazing code. It works, and if you want to try it, you can put it from here . But again: this is a demo, its purpose is not to be a perfect application, but an introduction to Kotlin for Android.

    Again, something new. And why should I know that?




    In order not to delay the most interesting, we mention only the most important advantages of Kotlin'a:
    • compact code. Quickly write, easy to read, fewer errors;
    • built-in error protection, first of all, nullability, i.e., the requirement for the programmer to explicitly indicate that a certain variable can be null;
    • ease of development (in my opinion, even a novice Java developer will master Kotlin without difficulty).

    On Kotlin'e you can write much faster than on Jav'e, and the code, in my opinion, is more beautiful.

    By the way, on github'e there are already more than 200 repositories with the code on Kotlin'e, and if we talk about mobile applications, then Kotlin was used to develop the Telegram messenger.

    Well, where do you start?


    To work, you need an IDE. For obvious reasons, I prefer IntelliJ IDEA, however JetBrains also makes a Kotlin plugin for Eclipse, and vim lovers can use a separate language compiler.

    Further we will assume that everyone who wishes to try Kotlin in business under Android will use IntelliJ IDEA. To work, you will need to install the Kotlin plugin and make sure that the computer has gradle and Android SDK (this has nothing to do with Kotlin'y, this is necessary for development for Android on anything).

    Java was first


    I already had a partially written application when I decided to finish it. Everything was written in Java.

    The essence of the application: on the screen there is a list of red tasks, if you click on a task, it turns green, and time has passed: this means that the user is working on a task. How to stop - click on it again, and the problem becomes red again, showing how not much time he spent on it. The application blindly trusts the user, without checking it in any way: this is not “big brother is watching you” from oDesk.

    What I wrote in Java, I wanted to rewrite it to Kotlin, my soul asked for 100% code for Kotlin, despite the fact that you can use Java and Kotlin code in the same project at the same time. It turned out that with legacy code everything is simple: next to src / main / javacreate the src / main / kotlin folder , and put the classes in it. Files in Kotlin don't end in .java , but in .kt . A pleasant moment turned out to be in place: Kotlin does not require the correspondence "one class - one file." You can cram as many classes into one file as you want according to the application logic. In my application there were only two logical parts - working with the database and the on-screen user interface, so that the number of files could be reduced.

    The Kotlin plugin for IntelliJ IDEA can convert .java files to .kt files by performing a neat translation from Java to Kotlin. You can do this through a right click on the file and the context menu, or you can right when copying Java code to a .kt file (the plugin will ask whether to convert).

    We write the code on Kotlin'e


    The Kotlin code is compact: for example, the class along with the constructor and getters / setters is described like this:

    class Item (val project: String, var status: String, var secondsSpent: Long, var lastactivated: Long)
    


    Let's look at the code that creates a dialog for entering text by clicking on a button.

    Java:
    addButton.setOnClickListener(new OnClickListener() {
      public void onClick(View v) { 
        AlertDialog.Builder alert = new AlertDialog.Builder (this);
        alert.setPositiveButton("OK", new DialogInterface.OnClickListener()  {
           public void onClick(DialogInterface dialog, int which)  {
                result = input.getText().toString()
           }      
        });  
      }  
    } 
    


    Kotlin:
    addButton.setOnClickListener() {
          var alert = AlertDialog.Builder(this);
          alert.setPositiveButton("ОК") {
             dialog, whichButton ->  result = input.getText().toString()
          }
     }
    


    Agree, the code on Kotlin'e is easier to read. For the attentive reader, we note that this, of course, is only a piece of code, since when creating a dialog, you need not only the OK button, but also the Cancel button. The code is fully uploaded to Github .

    What makes it possible to shorten the code? For example, in Kotlin, this form of writing is possible: if somewhere, as an argument, an instance of a class with one abstract method is expected, you can simply pass a lambda there: this is exactly what is shown in the example above.

    Please note that in Kotlin'e you can not specify the type of the variable, since it will be deduced from the right side of the assignment:

    JavaKotlin
    AlertDialog.Builder alert = new AlertDialog.Builder (this);val alert = AlertDialog.Builder (this)

    An interesting feature of the syntax in Kotlin'e: if the parameters are passed to the function, and the last parameter is the function, then it can be put out of brackets. This is exactly what we see in the fragment, where a lambda is passed to setPositiveButton, which will work by pressing the OK button:

    alert.setPositiveButton("ОК") { dialog, whichButton ->  result = input.getText().toString() }
    

    The same can be written as

    alert.setPositiveButton("ОК", { dialog, whichButton ->  result = input.getText().toString() } )
    

    You are free to choose which option seems easier to read.

    Using Java Libraries in Kotlin Code

    Another nice point is the ability to directly use libraries written in Java from Kotlin code.

    For example, to properly process special characters and Unicode in project names when saving them in SQLite, I used the StringEscapeUtils.escapeJava and StringEscapeUtils.unescapeJava functions from the popular library, which you just need to import with the import statement (and enter them in the project properties):

    import org.apache.commons.lang3.StringEscapeUtils
    

    Return value

    In Kotlin, it is forbidden to use return in functional literals (aka lambda functions), since a functional literal returns the value calculated in the last expression. So, in a call to setOnTouchListener, it is expected that the last parameter of the call is a function that returns boolean . This value actually returns gestureDetector.onTouchEvent (aEvent) .

    Java:
    gestureListener = new View.OnTouchListener() {
           public boolean onTouch(View v, MotionEvent event) {
                 return gestureDetector.onTouchEvent(event);
           }
    };
    

    Kotlin:
    getListView()?.setOnTouchListener() { v, aEvent -> gestureDetector.onTouchEvent(aEvent) }
    

    You cannot write return gestureDetector.onTouchEvent (aEvent) in the Kotlin functional literal code , the result of calling gestureDetector.onTouchEvent (aEvent) will be returned this way.

    Actually, there are special cases when a non-local return should be used in a functional literal. If interested, see the Kotlin documentation for details.


    The return of values ​​by functions defined through the "=" sign works similarly, like this:

    override fun getCount() = data.size()
    

    String patterns

    To get rid of long strings that are difficult to read and give non-optimal byte code, string templates are used in Kotlin'e:

    setMessage("Удаляем $projectToDelete?")
    

    Here projectToDelete is a string variable. The syntax for such a pattern is familiar to anyone who has been dealing with environment variables on UNIX, for example. Especially convenient are templates for strings composed of text and the values ​​of many variables, and even with formatting:

    secondsSpent.setText("${format("%02d", hours)}:${format("%02d",minutes)}:${format("%02d", seconds)}")
    

    Incidentally, an interesting story came up with formatting along the way: the String type in Kotlin is its own, and there is no format method in it, so I had to import java.lang.String.format explicitly and then refer to it like that. Surprisingly, by the way, there is still no secondsToHumanReadableString method in Java or Kotlin'e that converts an integer number of seconds to a format string
    HH: MM: SS.

    when and with

    It is very convenient to use when for multiple selection and with to reduce calls to methods and properties - both are enough even in a small application for Android. In my case, this provided easier-to-read code, for example:

    with (alert) {
       setPositiveButton("ОК") {
          dialog, whichButton ->
          ... тут код обработчика нажатия ОК ...
       }
       setNegativeButton("Отмена") { dialog, whichButton -> }
    // по "Отмене" делать ничего и не надо
       create()
       show()
    }
    

    Without with , alert.setPositiveButton, alert.setNegativeButton, alert.create, alert.show would turn out . When also improves readability:

    when (item.status) {
       "active" -> {
       item.status = "inactive"
       ... тут еще всякие важные штуки ...
       }
       "inactive" -> {
       item.status = "active"
       ... тут еще тоже всякое ...
       }
    }
    

    Compiling with Gradle


    If you create build.gradle with your hands and / or build a project with gradle without an IDE, read how to make friends gradle, Kotlin and Android on Kotlinlang.org .

    I don’t know if Android Studio users have such a problem, but for a person familiar with IntelliJ IDEA, working with Gradle may raise the question of how to assemble .apk for release, and not for debugging.

    For this, there is a Gradle plugin in IDEA, it is opened by clicking on the tab on the right:



    By default, IDEA collects * -debug-unsigned.apk , i.e. what you can’t put on Google Play. To make it * -release-signed.apk , you need to generate a signature key, put it in keystore, and enter a few lines inbuild.gradle your project so Gradle knows where to get the key. To build release, select the assembleRelease task in the Gradle window by double-clicking on it.

    You either already know about how to create a key and sign an application, or you can read more on stackoverflow .

    On a note


    Recently opened our new site about Kotlin kotlinlang.org , which is hosted on Jekyll + Github. Than this decision is good, recently already wrote on Habré .

    If some code examples seemed beautiful to you in the article, then this is an undoubted merit of Natalia Ukhorskaya , who works for us in the Kotlin team. I am very grateful to Natasha for the advice without which this article would be shorter, and the code - less like the classic Kotlin.

    We will be very happy if you can spend the hours saved by using Kotlin on the beach. Good rest of summer!

    UPD from 07/31/14, 18:55 : some terms and wordings are corrected.

    Also popular now: