As of October 1, 2023, LINE has been rebranded as LY Corporation. Visit the new blog of LY Corporation here: LY Corporation Tech Blog

Blog


Why we use Kotlin in LINE Creators Studio

Before we begin

Hi everyone, I am Freddie Wang from LINE Fukuoka's development team. I'm currently in charge of developing a new Android app called LINE Creators Studio. LINE Creators Studio is a sticker creation tool that lets anyone create his or her own stickers and sell them on LINE Store.

In this blog, I'd like to talk about Kotlin, the programming language which the LINE Creators Studio app is built on. I will explain why we chose Kotlin as our main language and introduce some of the Kotlin features we are using.

Note: LINE Creators Studio is currently available in Japan only. It will be released to other regions soon.

Advantages and key features of Kotlin

Why Kotlin?

At Google I/O 2017, Google announced that Kotlin is now officially supported in Android Studio 3.0. When we kicked off the development project for LINE Creators Studio at the end of 2016, we were tasked with the challenge of growing it into an MVP (Minimum Viable Product) project in a very short time. Although Kotlin 1.0 had been released quite some time ago, no one in our team had the experience of using Kotlin. We spent some time to conduct a survey on Kotlin before starting the development and eventually decided to use Kotlin because of the benefits it provides as described below.

100% interoperable with Java

The most appealing aspect of Kotlin is that you can mix Kotlin code and Java code in the same project and continue to use all existing Java libraries. Although we don’t have any legacy Java code in our project, we wanted to use some awesome libraries such as Dagger 2, Retrofit, and RxJava.

Concise syntax

Kotlin is designed for solving problems. One of its main goals is to write clean code more easily than Java. This is the first and foremost essential factor for us when developing Android applications.

Reduced dependencies

Kotlin has a compact runtime library which can replace some large Java libraries such as Guava. Using large libraries may not be a problem in server or desktop environments but it can be problematic in Android. Because the Android environment puts 65K methods limit on Android apps, we should avoid using large Java libraries. Kotlin's stdlib library (version 1.1.3-2) has only 6306 methods, which means it has less impact than the Guava libraries in terms of the number of methods.

Compatible with old Android devices

Kotlin 1.0 is based on Java 6, which means it can support Android devices whose version is 2.3 or higher. This is another very important factor for Android developers.

Key features of Kotlin

Nullability

Among all of Kotlin’s features, nullability support is our favorite one. We can use nullable variables in Kotlin code and let the Kotlin compiler check whether the variables are properly specified as nullable. In this way, we can keep source code cleaner, safer, and more readable. Here's a simple code sample of using null variables.

var output: String
output = null //compile complains

var output: String?
output = null //OK

//don't need to check null in every field
val name = bob?.department?.head?.name

Lambda expression

Another useful feature is lambda expressions. Although Java 8 does support lambda, only new Android devices support Java 8. On the other hand, Kotlin supports lambda and can run on most Android devices. We are actively using lambda expressions in the UI code of LINE Creators Studio. This has eliminated the need to implement too many listeners or use other view-binding libraries such as ButterKnife.

For example, we can create a custom back button as follows. It looks better than Java code, doesn’t it?

backButton = imageButton {
    id = R.id.my_back_button
    imageResource = R.drawable.ic_back
    onClick {
        onBackPressed()
    }
}

Extension

Extension is another awesome feature of Kotlin. It helps us to extend some Android frameworks without having to create a base class. For example, we can create an extension function for a Fragment class and use the function to swap one Fragment with other Fragment. Instead of creating a BaseCustomFragment class for each Fragment, we can let all Fragments use this Fragment extension.

fun Fragment.addFragment(fragment: Fragment) {
    fragmentManager.beginTransaction()
            .setCustomAnimations(R.anim.slide_left_in,
                                 R.anim.slide_left_out, 
                                 R.anim.slide_right_in,
                                 R.anim.slide_right_out)
            .add(R.id.container, fragment)
            .addToBackStack(fragment::class.java.simpleName)
            .hide(this)
            .commit()
}

//In another Fragment, don't need to write a BaseCustomFragment class
onClick { addFragment(AnotherFragment()) }

This is a simple example of converting Booleans to integers using the conversion extension. It is useful when you need to convert existing values to JSON-type data. You can easily replace Boolean values with integers.

fun Boolean.toInt(): Int {
    return if (this) 1 else 0
}

fun Int.toBoolean(): Boolean {
    return this != 0
}

//In another data class, we can use the extension like this
data.intValue = true.toInt()
data.booleanValue = 1.toBoolean() 

Named argument

Before using Kotlin, a function with several parameters (usually more than four) was a source of annoyance for all of us. However, if you use Kotlin's named arguments, you can write much more readable code. For example, we are using a Catmull-Rom spline function to provide a curve trimming tool in LINE Creators Studio. Since this function requires seven parameters, the function will probably become longer and more complex if we don't use named arguments.

fun catmullRomControlPoint(controlPoint: PointF, 
                           prevPoint: PointF,       
                           currentPoint: PointF,
                           nextPoint: PointF, 
                           delta1: Double, 
                           delta2: Double, 
                           alpha: Float) {
...
}

//When not using named arguments
catmullRomControlPoint(controlPoint1, 
                       point0, 
                       point1,
                       point2, 
                       delta1, 
                       delta2, 
                       alphaValue)

//When using named arguments
catmullRomControlPoint(controlPoint = controlPoint1, 
                       prevPoint = point0, 
                       currentPoint = point1,
                       nextPoint = point2, 
                       delta1 = delta1, 
                       delta2 = delta2, 
                       alpha = alphaValue)

Data class

Unlike other languages, Java doesn’t support struct so creating a POJO (Plain Old Java Object) is an irritating and tedious job for Java developers. Many Java developers use lombok to make the job easier, but you can make it even easier by using Kotlin's data class. All you have to do is create a data class as shown below. And after that, the Kotlin compiler will automatically generate getters and setters. The Kotlin compiler can also generate some basic functions such as equals(), hashCode() and copy().

//Just create the data class in one line, and that's it!
data class User(var name: String, var email: String, var age: Int)

you can also add annotations of gson.

data class User(@SerializedName("userName") var name: String, 
                @SerializedName("userEmail") var email: String, 
                var age: Int)

Anko

In addition to all these language features, Kotlin offers some useful libraries. Among them are stdlib and Anko (for Android); we use these two as the main libraries of the LINE Creators Studio project. Anko is one of the Kotlin libraries that provides several essential features including DSL (Domain Specific Language) for Android layouts, parsing for SQLite, helper functions for Android SDK, all of which serve as useful means to help Android developers work faster and easier.

The feature we use the most is Anko Layouts. It provides a DSL with which you can make Android UI more easily than using traditional XML. With Anko DSL, you can create a UI page like this.

//anko DSL
class MainActivityUi : AnkoComponent<MainActivity> {
    lateinit var aButton: Button
    override fun createView(ui: AnkoContext<MainActivity>): View = with(ui) {
        relativeLayout {
            aButton = button {
                textResource = R.string.ok
                onClick {
                    toast(“click button”)
                }
            }
        }.lparams(width = matchParent, height = wrapContent) {
            alignParentBottom()
        }
    }
}

The advantages of using Anko Layouts are:

  • More flexible than XML
  • Better performance

Because Anko creates all UI views programmatically, you can add conditions in the DSL using lambda expressions as follows. With XML, however, you may have to use a ViewStub or add a toolTipLayout into a RelativeLayout dynamically. For this reason, we do not use XML for writing layouts in the LINE Creators Studio project; the only exception of using XML is for specifying values. Also, our testing results show that the UI creation time has been shortened when compared to the time taken on older Android devices that did not use Anko DSL.

relativeLayout {
    ...
    if (shouldShowToolTip) {
        toolTipLayout()
    }
}

Another benefit of using Anko DSL is that developers can create UI layouts easily without having to rely on a UI preview tool. Although Anko does provide a UI preview tool for Android Studio and IntelliJ, the tool works only with Android Studio 2.4 or higher whereas we are using Android Studio 2.3. Even so, it doesn't bring us much trouble. With the help of the powerful Anko DSL, we can create our own UI layouts easily using structured syntax.

Anko provides some useful functions for running tasks asynchronously. For example, we use a doAsync() function to load bitmap to an image view.

doAsync {
    val bitmap = loadImage()
    uiThread {
        imageView.imageBitmap = bitmap
    }
}

Unit testing with Mockito

Unlike Java, all classes in Kotlin are final by default whereas Mockito can mock only those classes declared as open. This is bound to cause inconvenience for those trying to run unit tests. For instance, you may want to test whether a certain protocol is working properly between a client and a server. Since the inner working of the protocol depends on a specific data model, you cannot mock returning of response data if you are using Mockito. Fortunately, there are some solutions to this problem.

  • Use Mockito 2. However, you still need to create an org.mockito.plugins.MockMaker file.
  • Use the all-open plugin provided by Kotlin since 1.0.6 version
  • Use interfaces in code as much as possible

We decided to change the previously class-based source code to an interface-based one so that we can mock the project for unit testing. The change has brought us an additional benefit in that it helps us to avoid having "bad smell" in our source code.

Closing words

Before kicking of the LINE Creators Studio project, we had some doubts and concerns about using Kotlin. However, the more we wrote Kotlin code, the more we felt confident that we've made the right decision. Now all of our team members enjoy writing Kotlin code and the team productivity has improved greatly. If you haven't tried Kotlin yet, I recommend that you do. You won’t regret it!