ハトネコエ Web がくしゅうちょう

プログラミングやサーバー・Web制作、チームマネジメントなど得た技術のまとめ

シードって何? VC関連のメモ

スタートアップ界隈で仕事をしていれば、ジャフコとかインキュベイトファンドとか、
VC(ベンチャーキャピタル)の名前はよく聞く。

けれど彼らが何をしているのか私はよくわかっていない。
中の人に聞いてみるのが良さそうだけれど、まずは周辺知識についてまとめておく。

むっちゃわかりやすい参考記事 : 起業したてのシードスタートアップに投資する日本のVC(シードアクセラレーター/インキュベーター)さんをまとめました | TURN YOUR IDEAS INTO REALITY.

スタートアップのフェーズ

これについては調べるといろいろな区分けがあるが、
海外の記事を見るとだいたいこの4つだったので以下のまとめとする。(解説文は私の肌感)

  • シード (seed) : プロダクト作成のため、資金や協力者集めの段階。社員(?) 1〜2人程度
  • シリーズA : プロダクトは出来たがまだまだ会社は不安定。社員3〜10人程度
  • シリーズB : 資金繰りに安定が見えてきた。買収を持ちかけられることも。社員10〜30人程度
  • シリーズC : 事業の拡大が見えてきて、上場準備をおこなうため外部から積極的に役員を入れ始めたりする。社員50人〜程度

pre-seed, seed, post-seed, pre-A, Early A, A, Late A, B, ... などなど、細分化するといろんな呼び名がある。

参考: There are only three startup stages – Angular Ventures – Medium

……が、まあ、参考として貼った記事のタイトルが
『There are only three startup stages』であるように、大事なのは3つだ。
シード期シリーズAシリーズAを越えた後 の3つ。

実際、ベンチャー界隈の人と話すときは「シード」と「シリーズA」という言葉を覚えておけばなんとかなる。

シードからシリーズAあたりのことを「スタートアップ」と呼ぶこともあるので、
「『スタートアップ』の時期を乗り越えて……」という発言は「お金のない時期を乗り越えて……」という意味に解釈すればOK。
「スタートアップ界隈」と言うときは「ベンチャー界隈」と同義。
「スタートアップ」という言葉の範囲は広いのだ……。

ピッチイベント(ビジネスコンテスト)

IVS LaunchPadTechCrunch Tokyo などが有名なピッチイベント。
(参考: ピッチイベントまとめてみました。 | 起業サプリジャーナル

「起業したい、こんなアイディアがある!」という人たちの発表の場で、ここから投資が決まることも多い。
起業家からしてみれば、「このアイディア良さそうだけど、すでにありきたりなものじゃないか」「資金調達できるだろうか……」などの不安がある中で揉んでもらえるし、
投資家からしてみれば有望な投資先の発見につながるのでまさに Win-Win なイベント。

ピッチ (pitch) というのは短いプレゼンを指すらしい。
ピッチと言うと「ピッチャー(ボールを投げる人)」を連想するけど、「言葉を投げる」の意味合いもあるようで…。
(参考: 現役商社マンによる実践ビジネス英会話 #6 Pitch|WEBマガジンKEY-PRESS《キープレス》by三鬼商事

ピッチイベントへの参加を考えるなら、このテンプレートが役に立ちそう。
どのようなことをスライドに盛り込むべきか、わかりやすくまとめられている。↓

[スライド] スタートアップの 3 分ピッチテンプレート – Taka Umada – Medium

デット, エクイティ

デット (debt) は「負債」エクイティ (equity) は「株式」

参考: 投資の基礎知識「デット」と「エクイティ」の違いとは? | 富裕層向け資産防衛メディア | 幻冬舎ゴールドオンライン

起業家から見て日本政策金融公庫からの借り入れは debt で、
VCからの資金提供は equity 。

「エクイティ」という単語は、投資会社出身の人と話すとそこそこ出るワードなので覚えておくと便利。

VCの種類

ざっくり分けた。

例を挙げればキリがないのでかなり絞ったぞ!
思っていたよりいっぱいあるぞ!

ジャフコ野村證券系だったけれど、2017年7月に野村の持っていた株を買い*1
今は独立系となった様子。

CVCとして2019年2月に『DMM VENTURES』が誕生したのが最近のニュース。
( 「40以上の事業を持つ強みを生かせ」DMMがCVCをつくる理由 | STARTUP DB MEDIA | 日々進化する、成長産業領域に特化した情報プラットフォーム )

基本的にVCは資産運用を目的とするが、
CVCは事業シナジーを目的とする面もあるのが異なるところ。
(参考: 001 Startupの資金調達 — ピッチの相手はVCそれともCVC? – S. Nishimura – Medium

参考記事に

スタートアップからすると、特定の企業の資金を受け入れ、「色がつく」ことを避けたいという場合もある

とあるが、この「(資金調達により)色がつく」という表現を使うことはあるので覚えておくといい。

資金調達を受けるというのは、自分の会社の決定権の一部を売ることでもあるので、
どこから投資を受けるかというのは思いのほかセンシティブ。
(お金を調達できればいいじゃん、くらいに安直に考えていた……)

VCの資金をどうとらえるか、銀行融資ではないので返済する必要はありませんが、出資を受けることはある意味自分の株という魂の一部を渡すことと同じです。なので、それに対して何を求めるか、本当にお金なのか、一緒に経営していくことなのか、ノウハウなのか、よく判断したほうが良いとは思います。

http://www.jafco.co.jp/case/uuum より引用)

という一文がよく表している。


夜も遅くなってきたのでここまで。
ファンドの資金集め手法とか、1号, 2号とか、実際どのような業務が行われているのかとか、まだまだ謎が多い。

ベンチャーに投資して投資の運用益で食っていると思われがちなベンチャー・キャピタルのお仕事は意外にもファンドの固定手数料が食い扶持のメインなビジネス

http://www.turnyourideasintoreality.com/2013/12/vcinjapan より引用)

というのも実際そうなのか気になるところ。

VCやってる人に、どういう思いで何をして、どうやりくりをしているのか聞いてみたいなぁ。

CircleCI 2.0 で deploygate に Android アプリをアップロード

Android アプリを DeployGate
CircleCI を使って簡単にデプロイしたいな〜、と記事を探したら、
1.0 の記事が多かったので CircleCI 2.0 向けの記事を書くことにしました。

異なる証明書で署名されたアプリと言われ、deploygate に毎回アンインストールを求められる件についても、
「6. 異なる証明書で署名されたアプリと言われないために」の項で解決策を記しています。

実験台となったのは、
前回の『AndroidのgRPCクライアントをKotlinで実装しました』の記事で取り扱ったこちらのリポジトリ

1. config.yml はこうなった

まずは出来上がりから。
.circleci ディレクトリを作った上で、その中に config.yml という名前で保存します。

ただ、このあとの解説で書きますが、 DG_API_KEY を設定しないといけないので、
このままだとまだデプロイが出来ません。

aliases:
  android_docker: &android_docker
    docker:
      - image: circleci/android:api-28
        environment:
          TZ: Asia/Tokyo
  steps:
    - restore_cache: &restore_cache
        key: &jars_key jars-{{ checksum "build.gradle.kts" }}-{{ checksum "app/build.gradle.kts" }}
    - run: &download_deps
        name: Download dependencies
        command: ./gradlew androidDependencies
    - save_cache: &save_cache
        paths:
          - ~/.gradle
        key: *jars_key
    - run: &build_debug_apk
        name: Build debug APK
        command: ./gradlew assembleDebug
    - run: &test
        name: test
        command: ./gradlew test
    - run: &upload_to_deploy_gate
        name: Upload to DeployGate # Set DG_API_KEY in CircleCI
        command: |
          APK_PATH=app/build/outputs/apk/debug/app-debug.apk
          TIME=$(date "+%Y/%m/%d %H:%M")
          COMMIT_HASH=$(git log --format="%H" -n 1 | cut -c 1-8)
          USERNAME=nekonenene
          curl -F "file=@${APK_PATH}" -F "token=${DG_API_KEY}" -F "message=Build by CircleCI <${COMMIT_HASH}> (${TIME})" https://deploygate.com/api/users/${USERNAME}/apps

version: 2
jobs:
  build:
    <<: *android_docker
    steps:
      - checkout
      - restore_cache: *restore_cache
      - run: *download_deps
      - save_cache: *save_cache
      - run: *build_debug_apk
  test:
    <<: *android_docker
    steps:
      - checkout
      - restore_cache: *restore_cache
      - run: *download_deps
      - save_cache: *save_cache
      - run: *test
  deploy:
    <<: *android_docker
    steps:
      - checkout
      - restore_cache: *restore_cache
      - run: *download_deps
      - save_cache: *save_cache
      - run: *build_debug_apk
      - run: *upload_to_deploy_gate

workflows:
  version: 2
  build_and_deploy:
    jobs:
      - build
      - test
      - deploy:
          requires:
            - build
            - test
          filters:
            branches:
              only: master
          context: deploygate

(長いので gist で見たい人用リンク)

あとで再び書きますが、このリポジトリでは gradle 設定を Kotlin で書いているために、
「build.gradle.kts」と書いている箇所があります。
たいていの方は「build.gradle」と書き直してください。

また、docker image は「circleci/android:api-28」を使っていますが、
compileSdkVersion に合わせたものをお使いください。

2. master 以外のブランチではデプロイしない

他のブランチで push されたときもテストはしたいけど、
deploygate にアップロードするのは master ブランチへコミットがあったときに限定したいな〜
という希望があると思います。

それを実現しているのが workflows の部分です。

workflows:
  version: 2
  build_and_deploy:
    jobs:
      - build
      - test
      - deploy:
          requires:
            - build
            - test
          filters:
            branches:
              only: master
          context: deploygate

workflows としてはこうなっています。
ここの jobs で、実行する job を指定でき、各々にはさらに設定を書くことが出来ます。

- deploy:
    requires:
      - build
      - test
    filters:
      branches:
        only: master
    context: deploygate

filters: に branches: only: master と書かれていることによって、
master ブランチへコミットがあったときだけ、deploy job は実行されるようになっています。

また、 requires: に build と test が書かれていることによって、
build job と test job の両方が終わった後に deploy job が走るようになっています。

これを書かなかった場合、他の job と deploy job がパラレルに(並行して)走ります。

つまり、フローとしては以下のようになっています。

build ──┬── deploy (only: master)
test  ──┘

context については後で説明します。

3. 各 job を定義する

workflows から先に説明しましたが、もっとも大事なのは jobs の部分です。
ここで、何がおこなわれるかを定義します。

version: 2
jobs:
  build:
    <<: *android_docker
    steps:
      - checkout
      - restore_cache: *restore_cache
      - run: *download_deps
      - save_cache: *save_cache
      - run: *build_debug_apk

と書かれている部分ですね。

簡潔に書くため、YAMLのAnchors and Aliases 機能を使って書きましたが、
省略せずに書くと以下のようになっています。

jobs:
  build:
    docker:
      - image: circleci/android:api-28
        environment:
          TZ: Asia/Tokyo
    steps:
      - checkout
      - restore_cache:
          key: jars-{{ checksum "build.gradle.kts" }}-{{ checksum "app/build.gradle.kts" }}
      - run:
          name: Download dependencies
          command: ./gradlew androidDependencies
      - save_cache:
          paths:
            - ~/.gradle
          key: jars-{{ checksum "build.gradle.kts" }}-{{ checksum "app/build.gradle.kts" }}
      - run:
          name: Build debug APK
          command: ./gradlew assembleDebug

今回はビルドに使う Android SDK が 28 であるため、
使用する Docker image も circleci/android:api-28 を使用しています。

他のバージョンもありますので、ふさわしいものを探してください。
https://hub.docker.com/r/circleci/android/tags

restore_cache では、
build.gradle.kts および app/build.gradle.kts が更新されているかを確認し、
変更がなければ以前 save_cache で保存した .gradle を使用します。
これにより、2回目以降の CircleCI の実行時間が短くなります。

前回の記事でも触れましたが、 Gradle Kotlin DSL により
gradle 設定を Kotlin で書いているためにファイル名が「build.gradle.kts」になっています。

Gradle Kotlin DSL を使っていない場合は、 key: のところは
jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} と書きましょう。

4. deploygate にアップロード

肝心の deploygate へのアップロードですね!

このようにコマンドを作成しました。

- run: &upload_to_deploy_gate
    name: Upload to DeployGate # Set DG_API_KEY in CircleCI
    command: |
      APK_PATH=app/build/outputs/apk/debug/app-debug.apk
      TIME=$(date "+%Y/%m/%d %H:%M")
      COMMIT_HASH=$(git log --format="%H" -n 1 | cut -c 1-8)
      USERNAME=nekonenene
      curl -F "file=@${APK_PATH}" -F "token=${DG_API_KEY}" -F "message=Build by CircleCI <${COMMIT_HASH}> (${TIME})" https://deploygate.com/api/users/${USERNAME}/apps

deploygate の API ドキュメント によれば、
アプリのアップロードは

curl \
  -F "token=xxx" \
  -F "file=@sample.apk" \
  -F "message=sample" \
  https://deploygate.com/api/users/_your_name_/apps

で出来ると書かれていますのでそれに従っています。

変数 APK_PATH にデバッグビルドで作られるアプリのパスを指定、
message に入れる文字列として変数 TIME, COMMIT_HASH を定義、
変数 USERNAME には deploygate のユーザー名を代入したあと、API を叩いています。

"message=Build by CircleCI <${COMMIT_HASH}> (${TIME})" となっていますので、
「Build by CircleCI <02f18181> (2019/02/23 19:07)」のようなメッセージが付けられることになります。

deploygate の管理画面のスクリーンショット
deploygate の管理画面

メッセージに日時を入れているのは、
deploygate アプリからバージョン一覧を見たときに、どれが何か全然わからなかったためですね…。

↓ 入れないとこんな感じ

deploygate の Android アプリのスクリーンショット
deploygate の Android アプリのスクリーンショット

コミットメッセージを入れようかとも思いましたが、
コミットメッセージに特殊文字が含まれる場合に curl が失敗しないよう
処理を考えなくては…なので今回はやりませんでした。

5. deploygate API Key の指定

さて、上のコマンドで "token=${DG_API_KEY}" となっている箇所がありました。
ここの変数にどうやって値を入れているかの話をします。

CircleCI には、設定画面からプロジェクトごと、もしくは Context ごとに環境変数を設定できるようになっています。
今回はそれの後者、Context を使った環境変数の設定をおこなっています。
参考 : https://circleci.com/docs/2.0/env-vars/#setting-an-environment-variable-in-a-context

以下の画像のように、「SETTINGS」の「Contexts」へと進みます。

CircleCI の設定ページ
SETTINGS → Contexts

右上に「Create Context」のボタンがあると思いますので、それを押して Context の新規作成をします。
Name はわかりやすく「deploygate」としておきましょう。

f:id:nekonenene:20190223202240p:plain
「deploygate」の名前で CircleCI Context を作成

すると、以下の画面が出ますので「Add Environment Variable」ボタンから環境変数の追加をおこないます。

f:id:nekonenene:20190223202531p:plain

入力画面が出たら、Nameに「DG_API_KEY」、Valueに deploygate の API キーを入力します。

API キーは https://deploygate.com/settings の下の方に書かれています。

f:id:nekonenene:20190223202942p:plain
DG_API_KEY として deploygate の API key を設定

この設定を完了させたことにより、
(2. で説明を省きました) context: deploygate のコンフィグにより、
deploy job の実行時にいま設定した環境変数が読み込まれるようになります。

これで、 deploygate へ Android アプリを永遠にアップロードすることができる
CircleCI config が完成しました! おつかれさまです!!

6. 異なる証明書で署名されたアプリと言われないために

これで一件落着かと思いきや罠があります。

異なる証明書で署名されたアプリ (バージョン 1.0=1) が既にインストールされています。新しいアプリをインストールする前に、一度アンインストールしてください。

と、アンインストールが毎回求められてしまうことが発生します。

f:id:nekonenene:20190223203807j:plain
異なる証明書で署名されたアプリが既にインストールされています

なんで起きるかというと、デバッグビルドの署名は通常 ~/.android/debug.keystore にある証明書が使われるのですが、
CircleCI が使う Docker イメージが毎回異なるため、署名に使う証明書も一致しないのです。

ということで、 デバッグビルドに使う証明書を指定しましょう!!

まずはプロジェクトのルートディレクトリにて以下のコマンドを実行します。

keytool -v -genkey -keystore debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US" -keyalg RSA -validity 36500

これで 36500 日(およそ100年)有効の証明書が作成できます。

次に app ディレクトリの build.gradle.kts に以下のような設定が含まれるよう修正します。
参照: https://github.com/nekonenene/grpc_android_client/blob/master/app/build.gradle.kts

android {
    // 中略
    signingConfigs {
        getByName("debug") {
            storeFile = rootProject.file("debug.keystore")
            storePassword = "android"
            keyAlias = "androiddebugkey"
            keyPassword = "android"
        }
    }

    buildTypes {
        getByName("debug") {
            isMinifyEnabled = false
            signingConfig = signingConfigs.getByName("debug")
        }
    }
}

Gradle Kotlin DSL を使わない場合の書き方は異なります。公式のドキュメントなどを参照してください。
storePassword, keyAlias, keyPassword は、さっきのコマンドで指定した値(-storepass android -alias androiddebugkey -keypass android)と一致するように気を付けてください。

これにより、デバッグビルド時に、プロジェクトのルートディレクトリに作った
debug.keystore が毎回使われ、同一の証明書による署名となってくれるため、
deploygate にアンインストールが毎回求められる問題はなくなります。

7. おしまい

しっかり書いたらけっこう長くなりましたが、以上!
Android アプリを deploygate にデプロイし続ける方法でしたー!

異なる証明書で署名されたアプリ 問題は、昔「毎回アンインストールはめんどいな〜」って思いながらも
しぶしぶやっていましたが、今回調べたらちゃんと対処法が発見できてよかったです。

DroidKaigi 2018 のアプリ もよく見ると、
debug.keystore がトップディレクトリに置いてありますね。なるほどなー。

らくらく実機テストをして、楽しいアンドロイド開発ライフをお過ごしください!

AndroidのgRPCクライアントをKotlinで実装しました

このあいだの記事では、Go 言語による gRPC サーバーの実装を完了させました。
また、 Go 言語によるクライアント実装についても触れました。

実運用としてクライアント(データの受け取り手)となるものは、Web の他に、
AndroidiOS などのスマートフォンであることが多いかと思います。

というわけで、今回は、Android の gRPC クライアントKotlin で実装してみました。

gRPC Client for Android written in Kotlin
gRPC Client for Android written in Kotlin

1. 参考

以下が特に参考になりました。

また、出来上がりがこちらですので、
このあとの説明は、このコードを見ながらだとわかりやすいと思います。

2. proto を ビルドするための gradle の設定

最近は build.gradle を Groovy でなく Kotlin で書くことも流行っている気配なので Kotlin で書きました。
……が、そこまで大きなメリットがあるわけではなく、
少数派の書き方となってしまい情報が得づらくなるので、Android 開発に慣れていなければあまりオススメできません。

基本的には app ディレクトリ以下の build.gradle.kts を編集すれば良く、
grpc-java リポジトリにある実装例の build.gradle を、Kotlin 向けに書き直して以下のようにしました。

import com.google.protobuf.gradle.*
import org.jetbrains.kotlin.config.KotlinCompilerVersion

plugins {
    id("com.android.application")
    kotlin("android")
    kotlin("android.extensions")

    id("com.google.protobuf") version "0.8.8"
}

val grpcVersion = "1.18.0"
val protocVersion = "3.6.1"

android {
    compileSdkVersion(28)
    defaultConfig {
        applicationId = "net.hatone.hello_grpc"
        minSdkVersion(21)
        targetSdkVersion(28)
        versionCode = 1
        versionName = "1.0"
        testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        getByName("debug") {
            isMinifyEnabled = false
        }
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
        }
    }

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
    implementation(kotlin("stdlib-jdk8", KotlinCompilerVersion.VERSION))
    implementation("com.android.support:appcompat-v7:28.0.0")
    implementation("com.android.support.constraint:constraint-layout:1.1.3")

    // Test
    testImplementation("junit:junit:4.12")
    androidTestImplementation("com.android.support.test:runner:1.0.2")
    androidTestImplementation("com.android.support.test.espresso:espresso-core:3.0.2")

    // gRPC
    implementation("io.grpc:grpc-okhttp:$grpcVersion")
    implementation("io.grpc:grpc-protobuf-lite:$grpcVersion")
    implementation("io.grpc:grpc-stub:$grpcVersion")
    implementation("javax.annotation:javax.annotation-api:1.3.2")
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:$protocVersion"
    }

    plugins {
        id("javalite") {
            artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0"
        }
        id("grpc") {
            artifact = "io.grpc:protoc-gen-grpc-java:$grpcVersion"
        }
    }

    generateProtoTasks {
        all().forEach {
            it.plugins {
                id("javalite")
                id("grpc") {
                    option("lite")
                }
            }
        }
    }
}

https://github.com/nekonenene/grpc_android_client/blob/master/app/build.gradle.kts

ポイントは、plugins ブロックにある id("com.google.protobuf") version "0.8.8" です。
これにより、 protobuf-gradle-plugin が依存関係となり、
protobuf ブロックの使用が可能になっています。

なお、『API 'variant.getJavaCompile()' is obsolete and has been replaced with 'variant.getJavaCompileProvider()'.』という Warning がビルド時に出ますが、
おそらくこれは protobuf-gradle-plugin の問題によるもので、近い将来解決されるでしょう。

proto ファイルは app/src/main/proto ディレクトリ以下に置きます。
それ以外のディレクトリに置きたい場合は、 sourceSets ブロックに定義を書きましょう。
(参考: https://github.com/google/protobuf-gradle-plugin#customizing-source-directories

ビルドに成功すると、以下のように generatedJava フォルダの下に、
proto ファイルの定義に従って Java のクラスやインターフェイスが作られたことが確認できます。

f:id:nekonenene:20190222093401p:plain

generatedJava は便宜上のフォルダで、実際は
GreeterGrpc.javaapp/build/generated/source/proto/debug/grpc/io/grpc/examples/hello/ ディレクトリ下、
HelloProto.java などは ⁨app/build/generated/source/proto/debug/javalite/io/grpc/examples/hello/ ディレクトリ下に作られていました。

3. gRPC 通信をおこなう

これもまた、grpc-java リポジトリにある実装例の HelloworldActivity.java を Kotlin 向けに書き直せばいけます。
……が、このままだとさすがに汚いので、このコードで言うところの GrpcTask クラスは別ファイルに分け、
また、callback を設定し、Activity と疎結合になるようにしました。

https://github.com/nekonenene/grpc_android_client/tree/master/app/src/main/java/net/hatone/hello_grpc

やっていることとしては非常に単純で、

val channel = ManagedChannelBuilder
    .forAddress("grpc-test.hatone.net", 443)
    .useTransportSecurity()
    .build()
val stub = GreeterGrpc.newBlockingStub(channel)
val request = HelloRequest.newBuilder()
    .setName("Nanako")
    .setAge(75)
    .build()
val reply: HelloReply = stub.sayHello(request)
reply.message

このような書き方で簡単にレスポンスを取得できてしまいます。
GreeterGrpc クラスなどが自動生成されてくれるおかげで楽ができるのです。

SSL/TLS 通信をおこなうので、ManagedChannel 作成の際に
usePlaintext() でなく useTransportSecurity() メソッドを使うようになっている点にご注意ください。

また、インターネット通信をおこなうので、 AndroidManifest.xml

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

の1行を追加しておくことを忘れないようにしてください。

4. おわりに

もうちょっと書くことがある気もするんですが、ブログにしてみると意外とこんなものでした。

proto ファイルは今回は gRPC サーバー側の hello.proto を単純にコピーしてきましたが、
同一である必要があるので、別リポジトリを作って管理し、
submodule として各々が最新を引っ張ってくる作りにしておくと良いと思います。

久しぶりに Android アプリのコードを書いたので何度か悩みましたが、
それなりにきれいな実装に出来たと思うのでよかったです。
今回は AsyncTask を使いましたが、 RxJavaRxKotlin)も使ってみたいですね!