はじめに
こんにちは。LINE Fukuokaの開発チームで働いているFreddie Wangです。LINE Creators Studioという、誰でもオリジナルのスタンプを作ることができるようサポートするスタンプ制作ツールのAndroidアプリ開発を担当しています。LINE Creators Studioで作成したスタンプはLINE Storeで販売することができ、LINEを利用しているすべてのユーザーが購入できます。
今回の記事では、LINE Creators Studioアプリの開発に全面的に採用しているプログラミング言語「Kotlin」についてご紹介します。Kotlinをメイン言語として選んだ理由と主に使っているKotlinの機能を説明します。
Kotlinの利点と主要機能
Google I/O 2017において、GoogleはAndroid Studio 3.0からKotlinを公式にサポートすることを発表しました。LINE Creators Studio開発プロジェクトをキックオフしたのは2016年末でしたが、当時は、短期間でこのプロジェクトをMVP(Minimum Viable Product)プロジェクトに成長させないといけない課題を抱えていました。Kotlin 1.0がリリースされてからかなりの時間が経っていましたが、チームの中にはKotlinを使ってみた経験のあるメンバーがいませんでした。そのため、開発に着手する前にまずKotlinについて調べてみました。その結果、以下のような利点からKotlinを採用することにしました。
Kotlinを選んだ背景
Javaとの互換性は100%
Kotlinの最大の魅力は、KotlinコードとJavaコードを一つのプロジェクト内で共存させることができ、既存のJavaライブラリをすべて使用できるということです。このプロジェクトにはJavaレガシーコードはありませんでしたが、Dagger 2、Retrofit、RxJavaといったJava互換ライブラリを使いたいという希望がありました。
簡潔な構文
Kotlinは問題解決のために設計された言語です。Kotlinの主な目標の一つは、簡潔なコードをJavaより簡単に書けることです。これは、Androidアプリ開発において欠かせない重要な要素です。
依存性の減少
Kotlinは、Guavaのようにサイズの大きいJavaライブラリに取って代わることのできるコンパクトなランタイムライブラリを持っています。大容量のライブラリは、サーバーやデスクトップ環境では大した問題ではありませんが、Androidでは問題を引き起こす可能性があります。Androidアプリを開発する際にはメソッド数が65Kを超えられないという制限があるので、大容量のJavaライブラリの利用は控える必要があります。Kotlinのstdlibライブラリ(バージョン1.1.3-2)はメソッド数が6306個なので、メソッド数による影響はGuavaライブラリより少なくなります。
旧バージョンのAndroid端末をサポート
Kotlin 1.0はJava 6を基準にしているので、バージョン2.3以上のAndroid端末をサポートできます。これもまた、Android開発者にとって重要なポイントです。
Kotlinの主要機能
Nullability
Kotlinの機能で一番お気に入りなのは、nullability対応です。Kotlinコードでは、nullable変数を使うことができ、Kotlinコンパイラで各変数がnullableに正しく指定されているかどうかを確認することができます。Nullable変数を使うと、より簡潔で安全なうえに読みやすいコードを作成できます。以下に、null変数を使った簡単なサンプルコードを示します。
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
もう一つの有用な機能は、lambda expression(ラムダ式)です。Java 8はlambda対応ではありますが、Java 8は最新のAndroid端末でのみサポートされるという制限があります。それに対し、Kotlinはlambdaに対応するだけでなく、ほとんどのAndroid端末で動作します。そのため、私たちはLINE Creators StudioのUIコードにlambda expressionを積極的に使っています。そうすれば、listenerを無駄に多く実装したり、ButterKnifeのような他のview-bindingライブラリを使用したりする必要がなくなります。
例えば、以下のようにカスタム戻るボタンを作ることができます。Javaコードより分かりやすいと思いますが、いかがでしょうか。
backButton = imageButton {
id = R.id.my_back_button
imageResource = R.drawable.ic_back
onClick {
onBackPressed()
}
}
Extension
ExtensionもKotlinの代表的な便利な機能の一つです。Extensionを使えば、baseクラスを作成しなくてもAndroidフレームワークを拡張できます。例えば、Fragmentクラスのextension関数を作成し、その関数を使って一つのFragmentを他のFragmentとスワップすることができます。FragmentごとにBaseCustomFragmentクラスを作成する代わりに、すべてのFragmentがこの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()) }
以下は、conversion extensionを使ってBooleanを定数に変換する簡単なサンプルコードです。このextensionは、既存の値をJSON形式のデータに変換したいときに有効です。Boolean値を簡単に定数に変えることができます。
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
Kotlinを使う前は、複数のパラメータ(主に4つ以上)を使用する関数がみんなの悩みの種でした。しかし、Kotlinのnamed argumentを使えば、より読みやすいコードを書くことができます。LINE Creators Studioでは曲線トリミングツールを提供するためにCatmull-Rom spline関数を使っていますが、この関数は7つのパラメータを必要とします。もしnamed argumentを使わなかったとすれば、コードは非常に長くて複雑になったはずです。
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
他の言語とは違ってJavaはstructをサポートしないので、Java開発者にとってPOJO(Plain Old Java Object)を作成する作業は面倒で厄介なことです。数多くのJava開発者はこの作業をより簡単にするためにlombokを使っていますが、Kotlinのdataクラスを使うとさらに楽になります。以下に示すようにdataクラスを作成するだけです。そうすれば、Kotlinコンパイラがgetterとsetterを自動的に作成します。Kotlinコンパイラはequals()
、hashCode()
、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)
また、gsonのannotationも使用できます。
data class User(@SerializedName("userName") var name: String,
@SerializedName("userEmail") var email: String,
var age: Int)
Anko
Kotlinは言語機能の他にも複数の便利なライブラリを提供します。LINE Creators Studioで主に使っているライブラリは、stdlibとAnko(Android向け)です。AnkoはKotlinのライブラリの一つですが、AndroidレイアウトのためのDSL(Domain Specific Language)、SQLiteのためのパーシング、Android SDKのためのhelper関数など、様々な重要な機能を持っています。これらの機能はすべて、Android開発者がより迅速かつ簡単に作業できるようにサポートしてくれます。
私たちが一番よく使う機能はAnko Layoutsです。この機能が提供するDSLを使用すれば、既存のXML方式よりもっと簡単にAndroid UIを作成することができます。以下のように、Anko DSLを使ってUIページを作成できます。
//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()
}
}
}
- XMLより優れた柔軟性
- 性能向上
AnkoはすべてのUIビューをプログラミングで作成するので、以下のようにlambda expressionを使ってDSLに条件を追加することができます。しかし、XMLを使用すると、ViewStubを用いるか、toolTipLayoutをRelativeLayoutに動的に追加しなければなりません。そのため、LINE Creators Studioプロジェクトではレイアウト作成にXMLを使用していません。値を指定するときに限って、例外的にXMLを使います。ちなみに、テスト結果からも、Anko DSLXMLを使っていなかった古いAndroid端末に比べてUI作成にかかる時間が短縮されることが分かりました。
relativeLayout {
...
if (shouldShowToolTip) {
toolTipLayout()
}
}
Anko DSLを使って得られるもう一つのメリットは、UIプレビューツールに依存しなくてもUIレイアウトを簡単に作成できるということです。AnkoはAndroid StudioとIntelliJのためのUIプレビューツールを提供してはいます。しかし、このツールはAndroid Studio 2.4以上でのみ動作しますが、私たちはAndroid Studio 2.3を使っています。でも、これは大した問題ではありません。Anko DSLを使えば、構造化構文を使用する独自のUIレイアウトを簡単に作成することができます。
Ankoは、非同期タスクの実行に有効な関数を複数提供します。例えば、doAsync()
関数を使ってbitmapをイメージビューでロードします。
doAsync {
val bitmap = loadImage()
uiThread {
imageView.imageBitmap = bitmap
}
}
Mockitoを利用した単体テスト
final
ですが、Mockitoはopen
と宣言されたクラスのみmockできます。これは、単体テスト(unit test)を困難にする要因になります。あるプロトコルがクライアントとサーバー間で正しく動作するかどうかをテストすると仮定してみましょう。プロトコルの内部動作は特定のデータモデルによって異なってくるので、Mockitoを利用してレスポンスデータの返還をmockすることはできません。でも幸いなことに、いくつかの解決策があります。
- Mockito 2を使う。この場合、やはりorg.mockito.plugins.MockMakerファイルを使う必要がある。
- Kotlin 1.0.6から提供されるall-openプラグインを使う。
- コードにインターフェースを最大限用いる。
私たちは結局、プロジェクトをmockできるようにして単体テストを実施するために、既存のクラスベースのソースコードをインターフェースベースに修正することにしました。そこで、それを修正したおかげで得られたメリットがもう一つあります。ソースコードから「不吉な臭い(bad smell)」がすることを防止できるようになったのです。
おわりに
LINE Creators Studioプロジェクトを開始するまでは、Kotlinを使うことに不安がありました。しかし、Kotlinコードを使えば使うほど、正解だったという確信が持てました。今はチームメンバー全員がKotlinコード愛用者になり、チームの生産性も大きく伸びました。まだKotlinを使ったことのない方は、是非トライしてみてください。後悔することはないはずです。