雑に描いた餅

餅は餅屋、わたしは技術屋、と言いたかった。

Android StudioがUSB接続したAndroid端末を認識しない場合

小ネタ。

Android Studioを使ってAndroidアプリの開発をしてみようと、Android Studioや手持ちのAndroid端末(Google Pixel 3)をセットアップしていたときにぶつかったのでメモとしてのこします。

codelabs.developers.google.com

このチュートリアルに従ってAndroid Studioの設定を行っており、せっかくPixelが手元にあったので作ったアプリケーションを実際に動かしてみようとセットアップした際にハマりました。

やったこと

起こったこと

  • 起動デバイスを選択するプルダウン(スクショ右上)に「Google Pixel 3」が現れない
  • 「Troubleshoot Device Connections」を選択すると「Google Inc. Pixel 3」が現れたもののAndroid端末として認識されない

f:id:i-whammy:20200704182209p:plain
Google Pixel 3 is not recognized as an Android device!

対策としてやったこと

  • Google Pixel 3で開発者向けオプションを有効にする
    • 設定 > デバイス情報 > ビルド番号を7回タップ
  • Google Pixel 3の方の設定を以下のように変更
    • システム > 開発者向けオプション > USBデバッグを有効にする
    • システム > 開発者向けオプション > デフォルトのUSB設定 > ファイル転送/ Android Autoを選択

結果Pixel 3が無事にAndroid端末として認識されるようになり、アプリを動かすことが出来るようになりました。

参考

Pixel 3 doesn't recognize any USB connection - Pixel Phone Community

Kodeinに入門する

Kodeinについて

KodeinはKotlinで書かれたシンプルなDI Frameworkです。

https://kodein.org/di/

以前こちらの記事を書いたときにDI Frameworkとして採用してみましたが、なかなか使いやすかったので別記事でまとめてみようと思いました。

i-whammy.hatenablog.com

特徴

Kodeinは以下のような特徴を持っています。

  • シンプルで宣言的なDSLが利用できる
  • モジュール化によって依存関係を分割、明瞭に表現できる
  • JVM上のみならずAndroid等他プラットフォームでも動作する

導入

こちらのスタートアップガイドに沿って進めます。

https://kodein.org/Kodein-DI/index.html?latest/getting-started#_with_maven

今回はMavenを利用してアプリケーションを作成しようと思うので、以下のようにrepositoryの情報と依存関係を追加してあげましょう。

<repositories>
    <repository>
      <id>jcenter</id>
      <url>https://jcenter.bintray.com</url>
    </repository>
</repositories>
<dependencies>
    <dependency>
        <groupId>org.kodein.di</groupId>
        <artifactId>kodein-di-generic-jvm</artifactId>
        <version>6.5.0</version>
    </dependency>
</dependencies>

基本的な機能の紹介

Kodeinの基本的な機能を以下に簡単に紹介します。

Binding

以下のようなKodeinブロックの中で、KodeinのDI Containerの初期化を行うことができます。

val kodein = Kodein {
    bind<Database>() with singleton { SQLiteDatabase() }
}

上記の例では、Database型に対してSQLiteDatabase型の実装をシングルトンでバインドしています。

これによってKodeinからDabatase型の実装を呼び出す際、シングルトンとして生成されたSQLiteDatabaseのインスタンスを返してくれるようになります。

Singleton/Multiton/Provider/Factory

KodeinのBindingには以下のような種類があります。

  • singleton
  • multiton
  • provider
  • factory

singletonは文字通り、一度だけインスタンスを生成するような型に対して利用します。バインドされた関数はインスタンスの初期化時のみ呼び出しがされます。

一方でmultitonなオブジェクトは、同じパラメータが渡される限り、同じインスタンスを返すことを保証します。

singletonとの違いは、バインドされた関数が引数を取れるか否かです。

singletonが引数を取らず指定された型のオブジェクトを返すのに対し、multitonは5つまで引数をとることができます。

val kodein = Kodein {
    bind<DataSource>() with singleton { SqliteDS.open("path/to/file") }
    bind<RandomGenerator>() with multiton { max: Int -> SecureRandomGenerator(max) }
}

providerは上記の二つとは少し毛色が異なり、インスタンスの呼び出しがされるたびに毎回バインドされた引数なしの関数が実行されます。

これに対してfactoryはmultitonのように、最大5つまで引数を取るような関数が毎回実行されるようになっています。

またそれぞれの生成方法(主にSingleton)に対していくつかのオプションが存在しています。

  • Non-synced
    • Singleton, Multitonの場合に利用可能。デフォルトでは無効になっている。
    • 通常同期的に生成しているインスタンスを非同期で生成するようになる
      • 起動時のパフォーマンス改善に利用できるようだが、メリットデメリットが釣り合うケースがあまりイメージできなかった
  • Eager Singleton
    • Singletonにバインドした関数について、Kodeinインスタンスの初期化完了後すぐに実行するようになる
      • デフォルトでは無効になっており、インスタンスの呼び出しがされたタイミングで実行されるようになっている

transitive dependency(推移的依存)

Kodeinで生成したいインスタンスが、別のKodeinが生成するインスタンスに依存しているような場合、instance()を利用することでKodeinが依存関係の解決をしてくれます。

Clean Architectureを採用しているのであれば、Usecase層のクラスがインターフェースを介してRepository層を利用したり、Repository層のクラスがインターフェースを介してDriver層のクラスを利用するようなケースがよくあるかと思います。

そのような場合にも、Kodeinの内部のみで簡潔に依存関係の解決を行うことができるようになっています。

val kodein = Kodein {
    bind<IArticleDriver>() with singleton { ArticleDriver() }
    // ArticleRepositoryはIArticleDriverに依存している
    bind<IArticleRepository>() with singleton { ArticleRepository(instance()) }
    // ArticleUsecaseはIArticleRepositoryに依存している
    bind<IArticleUsecase>() with singleton { ArticleUsecase(instance()) }
}

module

個人的にKodein最大の特徴だと思っているのがこのModuleです。

Kodeinは一つのグローバルなオブジェクトに全ての依存関係を追加することもできますが、アプリケーションが大きくなるごとにKodeinオブジェクトが管理する依存関係も増えていき、複雑度が増していきます。

これに対して、Moduleを利用することで依存関係を適切な単位に分割することができるようになります。

Moduleは以下のように宣言することができます。

val articleModule = Kodein.Module(name = "article") {
    bind<IArticleAPI>() with singleton { ArticleAPIImpl() }
}

宣言したModuleは以下のように親となるKodeinオブジェクトにインポートすることができます。

val kodein = Kodein {
    import(articleModule)
}

ドメイン駆動設計を利用した開発を行なっているのであれば、境界づけられたコンテキストに沿ってこのModuleを分割することもできるかと思います。

tag

例えば同じ型に対して複数の実装が存在し、何らかの条件によって呼び出す実装を使い分けたい場合があるかと思います。

この場合、Kodeinのtagという機能を利用することで、呼び出す実装を使い分けることができます。

以下の例では、ウェブアプリケーションから呼び出された場合とモバイルアプリから呼び出された場合とで異なる接続情報を利用することを想定して、タグを利用して実装の呼び出しを分けるようにしています。

val kodein = Kodein {    
    bind<Configuration>(tag="mobile") with singleton { MobileConfiguration() }
    bind<Configuration>(tag="web") with singleton { WebConfiguration() }
}

Injection vs Retrieval

宣言したインスタンスを呼び出す場合に、Kodeinは二種類の呼び出し方をサポートしています。

一つはクラスに自動的に依存性が注入されるようなやり方(公式ドキュメントでは Dependency Injection と呼んでいます)、もう一つはクラス生成時に自ら依存性を収集しにいくようなやり方(同じく公式ドキュメントは Dependency Retrieval ")です。

Injectionを行う場合は、以下のようにnewInstance()を利用して呼び出すことでインスタンスの生成が行えます。

val articleUsecase by articleDependency.newInstance { ArticleUsecase(instance()) }

このやり方のメリットは、クラスがKodeinのことを知る必要がない、つまりクラスはDI Containerが何であるかといった事情から解放されるようになります。

一方でRetrievalを利用する場合、Kodeinコンテナからinstance(), provider()などを使って、予め設定されているインスタンスの生成方法にのっとってインスタンスを生成することになります。

val articleUsecase by articleDependency.instance<ArticleUsecase>()

Injectionと比較すると、コンテナに設定したインスタンスの設定方法に依存した形でインスタンスの生成を行うことになります。

なおどちらの場合であっても、インスタンスの生成はコンテナ起動時ではなく、インスタンスの呼び出し時に行われます。

サンプル

今回は以下のような簡単なサンプルアプリケーションを作って実際にKodeinの機能にいくつか触れてみました。

https://github.com/i-whammy/kodein-sample

あくまで感じを掴むことを目的としているので、実装は相当簡素なものになっています。

今回はモバイル、ウェブ両方から呼び出される簡素なAPIサーバーを想定して作成しました。フレームワークはKtorを利用しています。

以下Kodeinに関わっている主なクラスを簡単に説明していきます。

SampleApplication.kt

fun main(args: Array<String>) {
    val server = embeddedServer(Netty, port = 8080) {
        val kodein = Kodein {
            import(DependencyProvider.provideConfigurationDependency())
            import(DependencyProvider.provideArticleDependency())
        }
        val configuration by kodein.instance<Configuration>(tag = args[0])
        configuration.load()

        install(ContentNegotiation) {
            jackson {  }
        }
        routing {
            get("/articles") {
                val usecase by kodein.newInstance<IArticleUsecase> { ArticleUsecase(instance()) }
                call.respond(usecase.getArticles().first())
            }
        }
    }
    server.start(wait = true)
}

今回のアプリケーションを起動する関数です。

Ktorを利用したAPIサーバーの開発については以前の記事にて記載したので今回は割愛します。

ポイントはKodeinオブジェクトの初期化時に、DependencyProviderから異なる二種類のモジュールをインポートしている部分です。それぞれ設定に関わる部分と、アプリケーションのビジネスロジックに関わる部分とで依存関係を二つのモジュールに分けています。

その後、Configuration型のインスタンスについては起動時引数によって、Kodeinにインスタンスの生成を任せる形で対応するインスタンスを生成しています。

一方、IArticleUsecase型のインスタンスについては、こちらから依存性を注入する形で新しいインスタンスを生成しています。

DependencyProvider.kt

class DependencyProvider {
    companion object {
        fun provideArticleDependency() = articleDependency
        fun provideConfigurationDependency() = configurationDependency
    }
}

private val articleDependency = Kodein.Module(name="dependency") {
    bind<IArticleRepository>() with singleton { ArticleRepository() }
}

private val configurationDependency = Kodein.Module(name="configurationDependency") {
    bind<Configuration>(tag="mobile") with singleton { MobileConfiguration() }
    bind<Configuration>(tag="web") with singleton { WebConfiguration() }
}

今回は依存関係を管理するモジュールを生成するクラスをアプリケーション自体のクラスとは分けました。

アプリケーション起動時にarticleDependencyconfigurationDependencyを呼び出し、それぞれインポートできるような形にしています。

configurationDependencyの方はタグを利用して生成するインスタンスを分けられるようにしました。

感想

自分が以前使っていたSpring FrameworkのDIと比較すると、アプリケーション全体の構造をモジュールによって明示することができたり、実装と構造を切り離しやすい点が優れていると感じました。

アノテーションベースのDIはとっつきやすい反面、全体像が見えにくくなってしまったり、コードベース全体がDI Frameworkに依存するデメリットを持っていると思います。

今回の記事の中で触れたようなKodeinの特徴を利用することで、アプリケーション全体の構造を可視化したり、フレームワークへの依存度を下げることができるのではないか、と考えました。

今後大規模なアプリケーションで利用した際にどのような問題が出てくるかは使いながら試していきたいと思います。

Spring Bootで実装したアプリケーションをKtorで置き換えてみる

Ktorについて

KtorはKotlinを利用した軽量なWeb Frameworkです。

https://ktor.io/

現在はJVM上で動作するサーバー、クライアントだけでなく、javascriptiOSAndroid上で動作するクライアントをサポートしています。

今後はNative Environmentでのサポートも行っていくということで、今後が楽しみなフレームワークの一つです。

この記事は

今回は以前Spring Bootで実装したアプリケーションをこのKtorを利用して置き換えてみようというのが記事の主旨です。

置き換える中で、Ktorの特徴やSpring Bootと比べた時の違いなどに触れられたら良いなと思っています。

実際のコードは以下のリポジトリにて公開しています。一つ目がSpring Bootで実装された元のプロジェクト、二つ目がKtorで置き換えた後のプロジェクトです。

github.com

github.com

対象読者

  • Kotlinを使ってweb applicationを書いたことがある、もしくは書いてみたい
  • ktorになんとなく興味がある、spring boot等他のフレームワークと比較してみたい

Ktorのセットアップ

以下のQuickstartに沿って進めます。今回は元のプロジェクトがMavenを利用していたので、同じくMavenを利用して開発を進めます。

https://ktor.io/quickstart/quickstart/maven.html

まずktorを利用してサーバーを起動させるのに必要な依存関係を追加していきます。

ktorではどのようなサーバーエンジンを利用するかを開発者が選択することができます。選択肢としては、Netty、Jetty、Tomcatが代表的なものです。

今回はNettyをエンジンとして利用するため、核となる ktor-server-core に加えて、 ktor-server-netty を依存関係として追加します。

<project>
    <repositories>
        <repository>
            <id>jcenter</id>
            <url>https://jcenter.bintray.com</url>
        </repository>
    </repositories>
    ...
    <dependency>
        <groupId>io.ktor</groupId>
        <artifactId>ktor-server-core</artifactId>
        <version>${ktor.version}</version>
    </dependency>
    <dependency>
        <groupId>io.ktor</groupId>
        <artifactId>ktor-server-netty</artifactId>
        <version>${ktor.version}</version>
    </dependency>
    ...
</project>

なおMaven Repositoryとしてjcenterを追加してやる必要があるので、忘れずに追加してやりましょう。

Ktorを利用してサーバーを起動する

Ktorでは以下のようにmain関数の中にサーバーを起動させるDSLを記載することで、簡単にHTTPサーバーを起動させることができます。

import io.ktor.application.*
import io.ktor.http.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*

fun main(args: Array<String>) {
    val server = embeddedServer(Netty, 8080) {
        routing {
            get("/") {
                call.respondText("Hello, world!", ContentType.Text.Html)
            }
        }
    }
    server.start(wait = true)
}

少し上記のコードを掘り下げてみます。

embeddedServer 関数には、第一引数にサーバーエンジンのFactory、第二引数にリクエストをリッスンするポート番号を渡してやることができます。 第三引数はデフォルトで 0.0.0.0 が渡されるようになっており、基本的には省略されます(上の例でも省略されています)。 そして第四引数に、アプリケーションコードを書いていくことになります。

上記のように作られたサーバーのインスタンスstart関数で起動させることで、 8080番ポートの/にGETのリクエストを投げると、"Hello, world!"という文字列を投げ返すようなセルフホストなサーバーを作ることができました。簡単ですね。

JSON

初期状態では、リクエストをJSON形式で受け取ったり、レスポンスをJSON形式で返したりすることができません。

Ktorでは、Featuresと呼ばれる機能を追加することで、これらの追加機能を実現しています。

JSONへのシリアライズ、デシリアライズは以下のサンプルのようにContent NegotiationというFeatureを利用することで実現されます。

    embeddedServer(Netty, 8080) {
        install(ContentNegotiation) {
            jackson {
            }
        }
        routing {
            get("/systems/ping") {
                call.respond(mapOf("message" to "pong"))
            }
        }
    }.start(wait = true)

なおJacksonを利用する場合には以下のように依存関係に追加してやる必要があるので忘れないようにします。

<project>
    ...
    <dependencies>
        <dependency>
            <groupId>io.ktor</groupId>
            <artifactId>ktor-jackson</artifactId>
            <version>${ktor.version}</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

上記の結果、/systems/pingに対してGETのリクエストを投げると、以下のようにJSON形式でレスポンスを得ることができるようになりました。

$ curl localhost:8080/systems/ping
{"message":"pong"}

LocalDateTime型を任意の形にフォーマットする

Spring Bootで実装していた際にはよしなにされていたもののKtorではうまく行かなかった部分として、JSON内の日付型のやりとりがあります。

例えばLocalDateTime型の値をサーバーから受け取る際、以下のように出力されてしまうという問題が発生していました。

"createdAt":{"dayOfWeek":"WEDNESDAY","dayOfYear":1,"month":"JANUARY","nano":0,"year":2020,"monthValue":1,"dayOfMonth":1,"hour":12,"minute":0,"second":0,"chronology":{"id":"ISO","calendarType":"iso8601"}}

LocalDateTimeの情報としては十分以上に受け取ることができていますが、そこまで必要ではなかったり、任意のフォーマットに変換してやりたくなりますね。

Ktorでは先ほどのjacksonブロック内に、JacksonのObjectMapperを設定するようなコードを書くことができます。

より正確にいえば、内部にObjectMapperをレシーバーとした関数を書くことができるので、以下のようにして日付を任意の形式にフォーマットするようなコードを書くことができます。

        install(ContentNegotiation) {
            val javaTimeModule = JavaTimeModule()
            javaTimeModule.addSerializer(LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")))
            jackson {
                registerModule(javaTimeModule)
            }

        }

(なおここで利用しているJavaTimeModuleはjackson-datatype-jsr310というライブラリに入っているので、依存関係を追加してやる必要があります。)

上記のようなmoduleを追加してやることで、以下のような出力を得ることができるようになりました。

"createdAt":"2020-01-01T12:00:00.000Z"

ルーティング

Ktorでは上記の例でも少し登場したroutingブロック内にルーティング関数を追加していくことで、ルーティングの定義を追加することができます。

今回は実際のコード例を利用しながら、Ktorを利用したルーティングについて解説してきます。

概要

上記のroutingブロックの中では、Routingクラスというクラスをレシーバーにした関数を書くことができます。

RoutingクラスはRouteクラスの拡張先の一つであり、Routeクラスには以下のようなルーティング関数が用意されています。

route関数

Routeに対してルーティングを定義します。

例えば以下のような実装であった場合、"/systems/ping"というルートに対して、ブロック内部の処理をバインディングしています。

        routing {
            route("/systems/ping") {
                call.respond(mapOf("message" to "pong"))
            }
        }

またroute関数はネストさせることができるため、以下のように共通するパスに対する処理を一つにまとめることができます。

        routing {
            route("/api/articles") {
                get("/") {
                    // 記事の一覧を取得する処理
                }
                post("/") {
                    // 新しく記事を登録する処理
                }
            }
        }

get/post/put/delete等各種HTTPメソッドに対応した関数

Ktorでは上記の他に、GET/POST/PUT/DELETE等各種HTTPメソッドに対応したルーティング関数が用意されています。

例えば以下の関数では"/systems/ping"に対するGETのリクエストに対して、ブロック内部の処理をバインドしています。

        routing {
            get("/systems/ping") {
                call.respond(mapOf("message" to "pong"))
            }
        }

Path Parameter

リクエストのパス内部にある変数を取得したい場合、以下のように{}で囲まれた変数と、callという変数の中にあるパラメータからパスパラメータを取得することができます

    route("/api/articles") {
            route("/{slug}") {
                get("/") {
                    val slug = call.parameters["slug"]!!
                    call.respond(articleUsecase.getArticle(slug).convertToArticleResponse())
                }
    }

上記の例では、/api/articles/{slug}というリクエストについて、slug部分に入る値をパラメータとして取得することができます。

Request Header

リクエストヘッダーの値を取得したい場合、パスパラメータの場合と似ているのですが、routeブロック内のcall変数から取得することができます。

        delete("/") {
            val slug = call.parameters["slug"]!!
            val authorizationHeader = call.request.headers["Authorization"]!!
            val userId = userService.getUserId(authorizationHeader)
            articleUsecase.delete(slug, userId)
        }

上記はリクエストヘッダのうちAuthorizationというキーの値を取得しようとしています。

Request/Response Body

リクエストボディやレスポンスボディは以下のように、リクエストやレスポンスに対応させたいClassを指定することができます。

これに加えて上記のJacksonのFeatureを有効にしていると、Classに対応した型からJSONへのシリアライズ、デシリアライズを行うことができるようになります。

        post("/") {
            val request = call.receive<ArticleRequest>()
            val authorizationHeader = call.request.headers["Authorization"]!!
            val userId = userService.getUserId(authorizationHeader)
            call.respond(
                articleUsecase.createNewArticle(userId, request.title, request.body).convertToArticleResponse()
            )
        }

少しわかりにくいかもしれませんが、call.receive<T>()の呼び出しでリクエストに対応させたいT型を指定し、レスポンスの型としてcall.respond(T)としてT型のインスタンスを渡してやっています。


個人的にはSpring BootやSpring Frameworkのようにアノテーションベースで記載をする方が見慣れているので、上記のようなDSLの記載には初めは若干戸惑いました。

慣れてくるとシンプルに見えてくるので、そこまで大きな問題はなくスムーズに開発ができたように思います。

Dependency Injection

Ktorにはデフォルトでは依存性の注入(Dependency Injection, 以下DI)をサポートする機能がありません。

そこで今回はKotlin製のDIコンテナであるKodeinを用いてDIを行うことにします。

(因みにKodeinはKOtlin Dependency INjectionのacronymみたいです。)

https://kodein.org/Kodein-DI/

val articleDependency = Kodein {
    bind<ArticleDriver>() with singleton { InMemoryArticleDriver() }
    bind<IArticleRepository>() with singleton { ArticleRepository(instance()) }
    bind<ArticleUsecase>() with singleton { ArticleUsecase(instance()) }
}

上記のコードの一行目では、ArticleDriverという型、インターフェースについて、InMemoryArticleDriverをシングルトンで注入する、というバインディングをおこなっています。

詳細は別の記事でもまとめようかと思いますが、Kodeinを利用することで、これまで@Component@Autowiredを駆使しておこなっていたDIを上記のようにすっきりと行うことができるようになりました。

Exception Handling

Springでは@ExceptionHandlerアノテーションを利用することで、特定の例外が発生した場合にどのようなレスポンスを返すか実装を決めることができました。

また@ControllerAdviceアノテーションによって、複数のControllerにまたがる例外処理を共通化することもできました。

Ktorでこのような実装を行いたい場合、先ほどJacksonをContentNegotiationとして追加したように、StatusPagesというFeatureを追加してやる必要があります。

fun main(args: Array<String>) {
    embeddedServer(Netty, 8080) {
        ...
        install(StatusPages) {
            exception<ArticleNotFoundException> { call.respond(HttpStatusCode.NotFound) }
        }
        routing {
            get("/systems/ping") {
                call.respond(mapOf("message" to "pong"))
            }
        }
    }.start(wait = true)
}

上記の例では、処理中にArticleNotFoundExceptionという例外が発生した場合に、レスポンスとしてステータスコード404(Not Found)を返すように実装したものです。

感想

Ktorはドキュメントが充実しています。そのため、ドキュメント通りに進めるだけでそれなりにスムーズに進められたのがよかったです。

(この記事に記載されている内容も、ほぼドキュメントに記載されているものからの抜粋だったりします。。)

一方気になった点として、デフォルトではログがほとんど出力されないようになっている点があります。

例えばアプリケーションの処理の中で内部エラーが発生しても、ログが全く出力されず、開発時に若干戸惑いを覚えました。

とはいえ現状でも機能がかなり充実しており、必要に応じてそれらの機能を組み合わせることで、不要な依存を持たなくて良いようになっている点が自分にとっては良いなと思いました。

今回は小さなアプリケーションで試してみたので、今後はもう少し規模の大きなアプリケーションで試すとどうなるか検討してみたいです。

Spring Bootとの比較という点で言えば、ある程度まとまった機能が最初から利用できるのがSpring Bootの良さであり、Ktorは必要に応じて機能をFeaturesや外部のライブラリを駆使して追加しなければならない分、面倒に感じてしまうこともあるかもしれないなと感じています。

個人的にはミニマルなフレームワークは好きなので、必要に応じて使い分けられるようになりたいです。