Infinito Nirone 7

白羽の矢を刺すスタイル

割とよくあるパターンでリアディレイラーを壊す実績を解除した話

おそらくスポーツ自転車を買った人なら誰もが、納車のタイミングでお店の人に説明を受けるであろう「ディレイラーハンガーが曲がる」パターンでリアディレイラーが昇天してしまう体験を通勤チャリに乗って帰宅している最中に体験したのでメモしておきます。

ディレイラーハンガー is 何

スポーツ自転車の変速機は外にむき出しで、かつ一点で支えられている上に結構長さがあるので、ほんのちょっとでも力がかかると簡単に折れ曲がります。このとき変速機が車体に直接くっついていると、一緒に車体まで巻き添えにしてしまい車体ごと買い換えないといけなくなって辛いので、ディレイラーハンガーというのを車体に取り付け、そのディレイラーハンガーに変速機(リアディレイラー)を取り付けることで、たとえ折れ曲がってもディレイラーハンガーで負荷を吸収して車体までダメージが行かないようにしています。ただしやはりリアディレイラーのついているところが急所であることには変わりないので、ディレイラーハンガーがほんのちょっとでも曲がってしまうと変速がおかしくなったり、最悪後ろの車輪にリアディレイラーが巻き込まれて破損します。

どのタイミングで壊れたのか

ちょうど渋滞のなかでユルユル進んでいる最中に漕ぎ出して間もないタイミングで壊れました。自転車の後ろからけたたましい音がして車輪がロックしたのですぐに止めて、あーこれが例のアレだってことに気が付きました。 スピードがそんなに出てなかったこともあって転倒するなどの事故にはならなかったので本当にホッとしています。

結果

f:id:KeithYokoma:20180323210856j:plain

このとおりです。スピードがそこまで出てなかったとはいえ車輪に巻き込まれたので見事にヒョエエエエエエッッ!!!!となってます。

壊れたのに気がついて道の脇に寄せて、とりあえずディレイラーを取り外せる大きさの六角レンチがないのでハンガーまるごとワイヤーも解除して車体から取り外しました。その時は気づきませんでしたが家に帰ってよく見てみるとハンガーがえらい勢いで曲がってたので、そりゃあ車輪に巻き込まれるよねって感じでした。

今後

車体にあうディレイラーハンガーを探すのと、ディレイラーそのものも修理不能なので買い換える必要があります。といってもしばらくはチャリ通ができなそうなのでぼちぼちやっていきます。

ちなみにほんのちょっとしたディレイラーハンガーの曲がりならお店の人に見てもらうと直してもらえます。ですが極端に曲がってると直せないですし、直せるレベルでも交換できるなら交換したほうがいいですね。アルミ製とはいえ力のかかり方によっては簡単に曲がってしまうので。

現場からは以上です。

CircleCI 2.0 で GCR に置いた private な Docker イメージを使ってビルドする

今日は Wercker がビルドキューを無限に積んでビルドしてくれなくなってしまったので、急遽 CircleCI 2.0 を試すことにしました。 メモリが足りずに OOM Killer にデーモンが殺されたり、desugar 中になにかが失敗しているようですが、とりあえず一番大事な「GCR にある private な Docker イメージでビルドを開始する」ことは可能なことがわかったのでよしです。公式にドキュメントが見当たらないのでこの記事で参考にしてください。

config.yml

Wercker の設定ファイルとほぼ同じように指定できますが、認証情報とタグの構造が違います。 認証情報は auth に持っていて、タグは image の部分に書きます。

version: 2
jobs:
  build:
    docker:
      # specify the version you desire here
      - image: gcr.io/your_account/your_registry_name:tag
        auth:
          username: _json_key
          password: $GCR_JSON_KEY_FILE

あとは環境変数の設定に GCR_JSON_KEY_FILE を追加すれば終わりです。

DroidKaigi 2018 やっていきed

「やっていき」だと未来の話だけど、やっていきが完了した話はどうやって言うのがいいか分からなかったので、edをつけてみました。

はい。DroidKaigi 2018 が無事に終わりました。自分の参加歴はスピーカーとしては 2 回目・スタッフとしては 4 回目(フル参戦)です。

自分の中での一番のハイライトはやはり、Romain Guy 氏と Chet Haase 氏と話ができたこと。今でもまだまだ英語力は足りなくて、このお二人のような気の利いたジョークは日本語でもままならないなって感じもするんですが、何はなくともとにかくいつも YouTube 等の画面の向こう側にいた「めちゃくちゃ話しを聞きたい人たち」が、自分の目の前にいていつでも話ができる状況にとてもテンションが上りました。たとえどんなにミーハーであろうとも、そして実際話したことはエンジニアリングとはあまり関係のないほぼ世間話のような内容だったとしても、これまでで一番貴重な体験になったようにおもいます。

スピーカーとしては、開催一週間くらい前までウンウン悩んでた資料がうまく(?)まとまって、当日は自分のよく知る話をひたすら 50 分ぶっ通しでやる実績を解除しました。去年は 30 分でしたが、今年はもう少し掘り下げたところも話しつつ全体像も捉えられるようにまとめて 50 分となりました。

speakerdeck.com

最後の最後、まとめで「これ実は Android Auto だけが使ってる話じゃないんだよね」と言うことで、タイトルが「Android Auto」である意味を盛大にひっくり返してしまいましたが、今思い返してもよくまぁこんなに内容てんこ盛りにしたなぁと自分でも思うので、次はやりすぎない程度に濃い話ができるようにしたいです。今後の目標は、どこかで完全英語版の発表をやることです。資料だけでなくスピーチも英語にするのが目標です。いやでも、過去に海外で完全英語版のプレゼンはしたことあるな……

年々 DroidKaigi の規模が大きくなってきており、それは参加者数だけでなく、会場の規模も、DroidKaigi に協賛してくださる企業の数も、そしてスタッフの人数も大きく成長し続けています。スタッフとしての事前の準備では会議を重ねて情報を共有し、知見のストックを Kibela で管理する流れが今年から導入されました。また、当日はインカムでものすごい量の情報があちこちから飛び交うので、大事な情報が流れていってしまわないよう運営アプリを作りました。各セッションルームの状況だったり、司会の人が話す内容の共有だったり、その他スタッフ全体に共有する情報を Push したりする目的でつくってみました。アプリの性質上公式アプリのように Google Play Store では公開していませんが、わりと公式アプリと同じような作りになっている部分もあったりします。技術カンファレンスの運営にカンファレンスで習得した技術を導入するというのは、言われてみればそういう目的でカンファレンスが開かれているのだから自然なことのようにも思いますが、今振り返ってみると結構面白い体験でした。

全日程が終了し、いろいろな方が思い思いの打ち上げを楽しんでいるのも見届け、今日は DroidKaigi Day3 ということで最後のお片付けをしました。カンファレンスで使う機材や資材には、会場からお借りしているもの・レンタル業者に依頼して借りているもの・自分たちが所有して使っているもの等といろいろあります。借りたものは所定の手順で返却をすればよいのですが、自分たちの所有しているものは自分たちで持って帰るか処分する必要があります。ある程度は宅配便等で保管場所に送ることもできますが、中には自分たちで倉庫に運ぶ必要のあるものもあります。今日はそういったものたちを運ぶ、ロジスティクスのお仕事の日でした。ハイエースクラスの車を運転した経験がここでいきるとは思ったこともなかったですが、これも貴重な体験でした。

また次回もやっていき!!

DroidKaigi 2018 Day 2 Room 5 で"詳解 Android Auto"という発表をします

droidkaigi.jp

気がついたら仕様の総ページ数が 200 を超えていて、超大作でお送りいたします。 Android Auto という取っ掛かりではありますが、幅広く応用の効く話を中心に据えていて、というか Android Auto の実体がほぼそういう潰しの効くフレームワークの上に成り立っているといって過言ではないのですが、そういう話をします。 2日目のランチ後という時間帯に50分の長丁場なセッションで、かつ自分が見たくて見たくて楽しみにしていたセッションが複数同時に走っていてん゛ん゛ん゛ん゛!゛!゛となっていますが、自分の発表も負けないくらい Android Auto とはなんぞやという話をします。

ちなみに先日の Shibuya.apk では Romain Guy 氏に握手してもらって最高でした!

ProductFlavor ごとに apply from で読み込む外部 Gradle ファイルを切り替える

flavor1flavor2 という 2 つの productFlavor があり、flavor ごとに名前が同じで値の異なる変数を使いたいとき、flavor1 向けの変数を定義した flavor1.gradleと flavor2 向けの変数を定義した flavor2.gradleを用意したとして、どのように apply from で読み込むファイルを切り替えるか。

flavor1.gradle

ext {
  versionMajor = 1
  versionMinor = 2
}

flavor2.gradle

ext {
  versionMajor = 1
  versionMinor = 2
}

versionCode や versionName は productFlavor ごとに変えられて、flavor1 や flavor2 といった flavor のなかで apply from が効くので次のようにします。

android {
  productFlavors {
    flavor1 {
      apply from: 'flavor1.gradle'
      versionCode = versionMajor * 100 + versionMinor
      versionName = "${versionMajor}.${versionMinor}"
    }
    flavor2 {
      apply from: 'flavor2.gradle'
      versionCode = versionMajor * 100 + versionMinor
      versionName = "${versionMajor}.${versionMinor}"
    }
  }
}

変数にしたのにそれぞれの productFlavor で versionCode と versionName を入れてやらないといけないのは冗長ですが仕方がない。。。

BuildType と ProductFlavor のマトリクスで BuildConfigField や ManifestPlaceholders を生成したい

BuildConfigField は、BuildConfig という自動生成されるクラスに定義する定数をビルドスクリプトから制御する仕組みです。 そしてもうひとつ、ManifestPlaceholders は AndroidManifest のプレースホルダーに放り込む値をビルドスクリプトから制御する仕組みです。

これらを活用すると、BuildVariants を変えただけで気軽にアプリの振る舞いを変えたり、ContentProvider の Authority の衝突を防いで異なる BuildVariants の apk を同じ端末内に共存させたりといったことができるようになります。

ContentProvider の衝突を防ぐという目的では、次のように debug と release のそれぞれの BuildType で別々の Authority を定義するのが一般的です。

android {
  buildTypes {
    debug {
      manifestPlaceholders = [
        authorityHogehogeProvider : "com.sample.hogehoge.debug.provider.HogehogeProvider"
      ]
      BuildConfigField "String", "AUTHORITY_HOGEHOGE_PROVIDER", "\"com.sample.hogehoge.debug.provider.HogehogeProvider\""
    }
    release {
      manifestPlaceholders = [
        authorityHogehogeProvider : "com.sample.hogehoge.provider.HogehogeProvider"
      ]
      buildConfigField "String", "AUTHORITY_HOGEHOGE_PROVIDER", "\"com.sample.hogehoge.provider.HogehogeProvider\""
    }
  }
}

こうしておけば、AndroidManifest.xmlは main ソースセット配下の一個だけで済みます。

<manifest>
  <application>
    <provider
      android:name=".provider.HogehogeProvider"
      android:authorities="${authorityHogehogeProvider}"
      android:label="HogehogeProvider"
      android:exported="false"/>
  </application>
</manifest>

ここで、この ContentProvider を flavor1 と flavor2 という2つの ProductFlavor でそれぞれ持つことを考えます。 先程の buildTypes で manifestPlaceholders を分けるコードのみでは、flavor1 と flavor2 とで ContentProvider が衝突するため共存できません。 ProductFlavor と BuildType の組み合わせで manifestPlaceholders や BuildConfigField を制御する場合は、次のようにすべての BuildVariants を走査して対応するものを差し込むロジックを書きます。

android {
  productFlavors {
    flavor1 {}
    flavor2 {}
  }

  buildTypes {
    debug {}
    release {}
  }

  applicationVariants.all { variant ->
    def flavorName = variant.productFlavors.get(0).name
    def typeName = variant.buildType.name

    if (flavorName.equals("flavor1")) {
      if (typeName.equals("debug") {
        // BuildVariant: flavor1Debug
        variant.mergedFlavor.manifestPlaceholders = [
          authorityHogehogeProvider : "com.sample.hogehoge.flavor1.debug.provider.HogehogeProvider"
        ]
        variant.buildConfigField "String", "AUTHORITY_HOGEHOGE_PROVIDER", "\"com.sample.hogehoge.flavor1.debug.provider.HogehogeProvider\""
      } else if (typeName.equals("release") {
        // BuildVariant: flavor1Release
        variant.mergedFlavor.manifestPlaceholders = [
          authorityHogehogeProvider : "com.sample.hogehoge.flavor1.release.provider.HogehogeProvider"
        ]
        variant.buildConfigField "String", "AUTHORITY_HOGEHOGE_PROVIDER", "\"com.sample.hogehoge.flavor1.release.provider.HogehogeProvider\""
      }
    } else if (flavorName.equals("flavor2") {
      if (typeName.equals("debug") {
        // BuildVariant: flavor2Debug
        variant.mergedFlavor.manifestPlaceholders = [
          authorityHogehogeProvider : "com.sample.hogehoge.flavor2.debug.provider.HogehogeProvider"
        ]
        variant.buildConfigField "String", "AUTHORITY_HOGEHOGE_PROVIDER", "\"com.sample.hogehoge.flavor2.debug.provider.HogehogeProvider\""
      } else if (typeName.equals("release") {
        // BuildVariant: flavor2Release
        variant.mergedFlavor.manifestPlaceholders = [
          authorityHogehogeProvider : "com.sample.hogehoge.flavor2.release.provider.HogehogeProvider"
        ]
        variant.buildConfigField "String", "AUTHORITY_HOGEHOGE_PROVIDER", "\"com.sample.hogehoge.flavor2.release.provider.HogehogeProvider\""
      }
    }
  }
}

ProductFlavor は複数定義可能なので、複数ある場合はvariant.productFlavors.get(n)nも適宜どの ProductFlavor に対して BuildConfigField や ManifestPlaceholders を差し込むかによって変えます。 BuildConfigField は variant が直接持っていますが、ManifestPlaceholders は variant.mergedFlavor 配下に対して設定します。variant.buildTypeにもmanifestPlaceholdersがありますが、こちらには設定できないことに注意しましょう。