Android
Kotlin
Ktor

この記事は リクルートライフスタイル Advent Calendar 2018 の9日目の記事です。

本日は、ホットペッパービューティーのAndroidアプリ開発を担当している @oxsoft が、KtorをAndroidで使ってみた話を書いてみたいと思います。

Ktorとは?

Kotlin製のWebフレームワークで、DSL形式で簡単に書くことができます。先日Ktor 1.0がリリースされました!
https://ktor.io/

公式のFAQにも書かれていますが、KtorはKotlinで書かれているので、Androidでも動きます。(ただし、API24以降)
https://ktor.io/quickstart/faq.html#android-support

今回作るもの

今回はKtorをAndroid上で動かして、簡単なモックサーバアプリを作ってみたいと思います。通常の開発用サーバに接続する場合と比較して、モックサーバアプリを作る利点としては以下のようなことが挙げられます。

  • 特殊なレスポンスも簡単にモックできる
  • エンジニア以外もGUIで簡単にレスポンスを変更できる
  • 端末単位で環境を用意することができる

もちろんKtorを使わなくてもAndroid上にHTTPサーバを立てる方法はいくつかありますが、今回はKtorをAndroidで使ってみたいという気持ちがあるのでKtorを使います :muscle:

環境構築

先述した通り、KtorはAPI24以降でしか動作しないので、まずはminSdkVersionを24以上にします

app/build.gradle
android {
    defaultConfig {
        minSdkVersion 24
    }
}

そして、以下の依存関係を追加します。

app/build.gradle
dependencies {
    implementation 'io.ktor:ktor-server-netty:1.0.0'
    implementation 'org.slf4j:slf4j-android:1.7.25' // ログ出力用
}

また、重複ファイルのエラーが出るので、以下のファイルを除外します。

app/build.gradle
android {
    packagingOptions {
        exclude 'META-INF/io.netty.versions.properties'
        exclude 'META-INF/INDEX.LIST'
    }
}

当然、インターネットアクセスが必要となるため、以下のパーミッションも追加します。

AndroidManifest.xml
<manifest>
    <uses-permission android:name="android.permission.INTERNET" />
</manifest>

サーバーの起動

以下のコードをバックグラウンドスレッドから呼び出すことで、8080ポートにHTTPサーバを起動することができます。

embeddedServer(Netty, 8080) {
    routing {
        get("/") {
            call.respondText("<h1>Hello world!</h1>", ContentType.Text.Html)
        }
    }
}.start(wait = true)

Chromeを開いてhttp://localhost:8080/にアクセスすると、無事にHello world!を表示する事ができました :tada:

また、CallLoggingを設定することにより、アクセスログを出力することができます。

embeddedServer(...) {
    install(CallLogging) {
        level = Level.INFO
    }
    ...
}
I/ktor.application: 200 OK: GET - /
I/ktor.application: Unhandled: GET - /favicon.ico

リクエストの受付

今回はassets/response/以下に用意したファイルを返却するようにします。URLのパスとassetsフォルダを対応させたいので、URLのパスをハンドリングします。パスのハンドリングは以下のように書くことで簡単に行うことができます。

get("/{path...}") {
    val path = call.request.path()
}

ルーティングのパラメータ名をpath...としていますが、今回は使っていないので実際には何でも大丈夫です。

もちろん、パラメータとしても受け取っているので、以下のようにしてもパスを取得することができます。(※スラッシュの有無などが、上記のパターンと異なります)

get("/{path...}") {
    val path = call.parameters.getAll("path").orEmpty().joinToString("/")
}

ルーティングの詳しい文法は、以下のドキュメントに書いてあります。
https://ktor.io/servers/features/routing.html

レスポンスの返却

あとは、このパスに応じてassetsファイルを返却するだけです。

以下のような関数を用意しておいて、

fun readAsset(path: String) = BufferedReader(InputStreamReader(assets.open(path))).use { reader ->
    reader.readLines().joinToString("\n")
}

レスポンス時に呼び出します。

call.respondText(readAsset("response$path"), ContentType.Application.Json)

これでモックサーバアプリは完成しました :dancer:

ホットペッパービューティーのデバッグ版アプリでは、OkHttp3のInterceptorによってドメインを任意のものに差し替えるデバッグ機能があるので、それをlocalhost:8080に指定することで接続できました :kissing_smiling_eyes:

実際のモックサーバアプリでは、以下のような感じでAPIごとにモックするレスポンスを変更することができるようにしましたが、特に記事にするほどのことをしていないので、今回は割愛します。

まとめ

ほとんどつまづくことなく、簡単にHTTPサーバを立てることができました :smile:

今年も残りわずかとなりましたが、来年もまたAndroid/Kotlin界隈がより一層盛り上がっていくのが楽しみですね!それではみなさん良いお年を :bamboo: