nashcft's blog

時々何か書く。

FlexiSpot 買って組んだログ

引越したのでデスクを新調した。会社では okamura の Swift を使っていて体験がとても良く、自宅にもスタンディングデスクを導入したいと思っていたのでこれを機に FlexiSpot E3 を購入。天板はかなでもののラバーウッドにした。

f:id:nashcft:20201126220045j:plain
完成後に適当にものを配置した時の様子

購入と準備

天板

当初の予定ではあまり工作をしたくなかったので FlexiSpot で天板セットを購入しようと思っていたのだが、購入当時欲しかった最大サイズの天板 (1600x700)が入荷待ちとなっていたので慌てて別の候補を探し、自分でやすり掛けや塗装などをしなくてよいいという理由でかなでものから購入することにした。この時に天板のサイズが大きくなった (1800x800)。

工具

購入後すぐに「flexispot 自作」「flexispot かなでもの」などで検索しまくって FlexiSpot のデスクを組み立てた先人たちの記録や天板にかなでものを選択した場合の留意点を漁ったり、会社の同僚からアドバイスを貰ったりしつつ準備をした。主に参考にしたものは以下:

パワーが必要とはいえ集合住宅なのでインパクトドライバは騒音的に避けた方が無難かなと思ったこと、スペック的には nabetama さんのブログにあった makita の DF333DSHX が良さそうだと思ったが amazon で配送日指定ができなかったこと、あとラバーウッドの硬さについてろくに調べずに、かなでもののサイトに「うちで扱ってる木材で一番硬い」と書いてあるのを読んでビビったことなどから、電動ドライバドリルは makita の DF483DRFX を購入した。ビット類はプラスドライバのビットが1つついてくるだけなのでいくつか適当にドライバビットとドリルビットも購入した。

マット

脚+天板で数十kgするし、流石に下に何か敷いておこうということで適当なマットを購入して敷いた。重量のあるパーツを置いた時の接地面がかなり小さいみたいなこともあったので組み立て作業時にも何かしら敷くものがあった方がよさそう。

組み立て

脚の組み立ては特に難しいことはなくすぐ終わった。昇降ユニットが重いので取り回しに注意した方が良い程度。ビームの長さは天板の幅1800mmに対して1600mmにした。

下穴を空けるのはドライバドリルの形状的に脚を天板に置いたままではできない箇所があったので、印をつけてから脚をずらして行った。最初の方は天板を貫通させないか心配で仕方なかったのでほんの少し削っては棒を挿して深さを測るみたいなことを何度も繰り返してて全然進まなかったのだけど、4, 5箇所空けたら感覚掴めてきたのでそれ以降はすんなり進められた。ところで世の中にはドリルストッパーとか深さ調整アダプタとかいう商品があるのでみなさんはそれを使いましょう。私はこれを書いてる最中に存在を知った。

最も気を揉んでいたネジ締めだが、電動ドライバで締める前に家にあったドライバで仮止めのような感じで軽く締めていたら思ったより感触が軽く、もしやと思いそのまま締め続けていたら最後まで苦労なく締めることができてしまった。とはいえ自分で回さない分電動ドライバの方が楽なんだろうと思ってそちらでも締めてみたが、ネジに掛かっている力の加減が掴めずビットがネジから外れたりネジ穴を潰したりしてしまわないかという不安が終始付き纏ったのと、その不安のため低速で締めていて時間短縮にならなかったので、じゃあ手で締めた方が気楽じゃんということで結局殆ど電動ドライバは使わなかった。これは慣れない*1道具だったのが原因だと思うので、使い慣れていれば電動ドライバの方が早くて楽に作業できるのかもしれない。

(空けてたか -> 空けてたが)

という感じであとはケーブル繋いでケーブルカバー取り付けて組み立て終了。

f:id:nashcft:20201126220057j:plain
組み立て終わってひっくり返す前のデスクと、かっこいいのに思ったより活用できなかった makita のドライバドリルセット

ひっくり返す際は、天板取り付け後なら他の自作ブログでもあるように少なくとも二人で作業した方がよさそう。今回は下にマットが敷かれてて、天板がマットからはみ出ていたので天板の下の隙間に指を引っ掛けて転がす要領で持ち上げることができたけど、床に直接置いてあった場合なんかは脚部分を引っ張り上げるみたいな感じになって、ネジを締めた部分に負荷かかって天板の重量によっては破損しそうだなって思った。

組み立てと設置に関するその他所感

  • 下穴は2.5mmでOK, 3mm だと少し大きいかもしれない
  • FlexiSpot に付属してたプラスドライバはNo.1っぽかったけどNo.2の方がフィットした
  • ビーム部分のネジ締めはネジ穴が凹型パーツの底にもあるので、ドライバビットにマグネット機能があると便利
  • 脚の接地面が面で支える形状になってないので少なくともパッドを挟んだ方がよさそう

使用感

  • 高さ変更時の動きはスムーズ
  • 高度を高くした時もそこまで揺れない
  • 高さ変更ボタンを押しっぱなしの状態にするのが難しい気がする、メモリ機能を活用した方がストレスがない

という感じで、ボタン操作以外はほとんど Swift と遜色ない感じだと思う*2。つまりたいへん体験が良い。あと広い天板はとても良いのでみなさんも FlexiSpot でデスク作るときは天板を無闇に広くしておくと良いと思う。

今後

  • 電源まわりを整備する
  • モニターを買う
  • MoonLander を買う
  • デスクマットあった方がいいかも
  • 椅子も買い換えたい
    • エンボディチェアかセイルチェアがいいなあ

*1:電動ドライバドリルを使うのは今回が初めてだった

*2:半年以上オフィスに行ってないので Swift の使用感はうろ覚え

Android Studio 4.2 から組み込まれる JDK のバージョンが 11 になるっぽい

現在最新の 4.2 Canary 13 で以下のようになっている。

f:id:nashcft:20201017203309p:plain
Android Studio 4.2 Canary 13 の "About Android Studio" のキャプチャ

前回書いた記事では当時の最新バージョンに組み込まれていたのが JDK 8 だったっぽいのと、手元にある記録からすると Canary 8 で上がったらしい。

f:id:nashcft:20201017205744p:plain
Android Studio 4.2 Canary 8 の "About Android Studio" のキャプチャ

Release Update には特に言及がない。

androidstudio.googleblog.com

CircleCI の Android Docker Image でJDKが11にアップデートされた件のまとめ

私は今回の件より前から CircleCI を使っておらず、最近は主に GitHub Actions でCIを構築していてこの件で被害は被っていないのだけど、軽く調べてみたところ Android project を JDK 9+ でビルドすることに強い興味をを持っている自分には結構興味深いことがわかったのでもう少し詳細に調べてまとめることにした。

何があったの

日本時間で 2020-08-18 のお話

  • CircleCI の Android Docker Image が更新され、JDK 8 ベースから JDK 11 ベースになった*1
  • これらの image を使ってCIを行っている Android project でビルドが失敗するものが発生した
  • Twitter が少し賑やかになった

原因1: なぜビルドが失敗するようになったの

この blog を書き始めてから調べて知った付け焼き刃な部分もあり正確な説明になっていない部分もあると思うが (詳しい方指摘ください...)、大雑把には JDK 9 で導入された Project Jigsaw*2 と呼ばれる module system に関する変更によって標準の package に破壊的な変更が加わり、それによってこれまでデフォルトでアクセスできた package にアクセスできなくなったことが原因である。

具体的には

  • DataBinding -> JAXB
    • JDK 9 でJAXBの package は deprecated となり、デフォルトで解決可能な classpath から外された*3
    • JDK 11 でJAXBを含む Java EEAPIJDKから削除された*4
  • JaCoCo -> jdk.internal.reflect
    • JDK 9 でデフォルトでアクセス不可能になった*5

という感じ。

原因2: なぜJDKのアップデートが行われたの

この変更は予告なく突然行われたわけではなく、実際には7月から変更のアナウンスが forum で発表されていた。

discuss.circleci.com

この投稿から辿っていくと、以下の issue がきっかけのように思われる。

github.com

この issue は targetSdkVersion を29にしているため Robolectric を使って実行するテストが Java 8 では動かないので、Java のバージョンを上げてくれというものだ。これは Robolectric 4.3 から適用される制約で、 release note で簡単に触れられているが、SDK 29 以降で Robolectric を使用する場合には runtime に Java 9 以上が要求され、Java 8 で実行するとエラーで止まるようになった。

https://github.com/robolectric/robolectric/releases/tag/robolectric-4.3

Running tests on Android Q requires Java 9.

https://github.com/robolectric/robolectric/releases/tag/robolectric-4.3.1

Running tests on Android API 29 now strictly requires a Java9 runtime or newer.

ところでこの問題自体は JDK のバージョンを上げなくても回避可能で、テスト実行時のSDKのバージョンを28以下に指定すればよく、 robolectric.properties に以下の設定を追加すれば良い:

# 28未満でも良い
sdk=28

これによって robolectric.properties が配置されている module での Robolectric を使ったテストでは指定した SDK version に基づいてテストを実行してくれる (この場合でも警告は出力される)。この指定をした場合でも、個別に @Config annotation で指定した SDK version が優先されるので影響は出ない*6

大半の場合はこれで解決するのでJDKのバージョンを上げる必要はないのだが、一つだけ上記の回避方法では解決できずに Java のバージョンを上げなければ解決しないケースがあり、それは minSdkVersion が29以上のときである。このケースでは robolectric.propertiessdk のバージョンを下げてしまうと targetSdkVersionminSdkVersion を下回る状態になってしまい下記のようなエラーになる。

java.lang.RuntimeException: android.content.pm.PackageParser$PackageParserException: <path to apk for local test>: Requires newer sdk version #29 (current version is #28)

解決方法

動かせた頃の image を使う

CircleCI Japan のアカウントで紹介されているように*7JDK 8 ベースだった時の docker image の digest を、使用する image の指定に追記することで使う image の固定ができる。今回の件で知ったが、これは document でも best practice として言及されている*8

docker:
  - image: circleci/android:api-29@sha256:<digest>

JDK 11 でもビルドできるように project を修正する

DataBinding

公式のアナウンスはまだなかった気がするが、 Android Gradle Plugin 4.1.0 で修正されているらしく、そちらでは JDK 11 を使っても DataBinding 関連のビルドが成功するようになっている*9*10。手元で動かしてみた感じでは 4.0.0, 4.0.1 でも同様にビルドできたので、4.0.0 にも backport されているらしい。

また、何らかの理由で AGP を 4.0.0 以上に上げられない場合は、JAXB関連の依存を自分の project に追加することでも解決できる:

dependencies {
  annotationProcessor 'javax.xml.bind:jaxb-api:2.3.1'
  annotationProcessor 'com.sun.xml.bind:jaxb-core:2.3.0.1'
  annotationProcessor 'com.sun.xml.bind:jaxb-impl:2.3.2'
  // kapt を使っている場合以下も追加する
  kapt 'javax.xml.bind:jaxb-api:2.3.1'
  kapt 'com.sun.xml.bind:jaxb-core:2.3.0.1'
  kapt 'com.sun.xml.bind:jaxb-impl:2.3.2'
}

JaCoCo

JaCoCo の設定に excludes という coverage 対象から除外するパターンを追加できるものがあるが、それに以下のように jdk.internal. から始まる package を追加することで解決可能*11*12

tasks.withType(Test) {
  jacoco {
    includeNoLocationClasses = true
    // excludes の一覧に 'jdk.internal.*' を追加
    excludes = ['jdk.internal.*']
  }
}

余談: JDK 11 になって嬉しいこともある

CircleCI で Java project といえばOOMだったりCPUの core を container に割り当てられた数よりも多く認識してしまったりという問題をどう対処するかという話があり、公式でも幾らかの言及がある*13*14。これはJVMの挙動の都合で cgroup による割り当て設定を見ないことが原因の一つなのだが、Java 10 で UseContainerSupport という VM option がデフォルト有効で導入され、cgroup で割り当てられた設定が反映されるようになり、また同じく追加された MaxRAMPercentage で割り当てられたRAMの量に対するパーセンテージで heap size の上限を定められるようになった。

この辺の話は本当に詳しくないので適当に記事のリンク貼ってお茶を濁させて。

www.eclipse.org

developers.redhat.com

merikan.com

medium.com

というわけで、もともと JDK 11 でもビルドできたり、先に述べたような対応をして JDK 11 でもビルドできるようにしたりした project は、上記の VM option の恩恵を得られるようになる。とはいえ昨今のCIを構築している Android project は大体が対策として Gradle の worker の数を絞ったり Xmx を設定したりしていると思うので、今なおRAMやCPUの問題で苦しんでいる人はそれほど多くないような気がするし、それらの対策のためにわざわざ JDK 11 対応をするにしてもモチベーションとしては弱いかもしれない。

雑感

この blog を書き始めた当初は AGP 4.0.0 以上でも JDK 11 で DataBinding がビルドできないものと思っていたので、こんな影響評価のお粗末なサービスは使わないで他のCIサービスに移行した方が良いですよみたいな締めくくりにして、ついでに移行先候補についても幾らか書こうと思っていた程度には個人的には印象が悪かった。その後調べた結果として DataBinding を捨てるなど大規模な修正なしに Android project 側でも対応可能なのがわかって、そこまで言う程ではないという評価に変わってその部分は削ったものの、実際はどうあれ CircleCI (の Android まわりに関わっている人) には JavaAndroid の情勢に詳しい人はいないんだろうなあ、という不信感は残った。

発端がユーザからのリクエストで、それに素早く対応する姿勢はとても好ましいものだと思う。しかし解決したいこと (Robolectric 4.3 以上を動かせるようにすること) に対してとった手段 (JDK 11 へのアップデート) によって発生した影響が釣り合っていないので、ちゃんと JDK のアップデート内容やそれによって起こりうる影響の調査をしたのだろうかという疑問がある。彼らは docker image の更新をリリースした当時 JDK 9 でJAXBのAPIにアクセスできなくなることが DataBinding のビルドに影響を与えることを知らなかったのだろうし*15、もしかしたら実行時の Android SDK のバージョンが29以上であることが必要でない限り Java 8 でも Robolectric 4.3 以上を使ったテストを実行できるようにする方法があることを今でも知らないかもしれない。そもそもまだ Android Studio に組み込まれている JDK は4.2 Canary でも JDK 8 で、Android アプリ開発の現場の大半では開発環境に JDK 8 を想定していると見て良いはずなのに、1つのライブラリに対応するために自分たちのメインストリームをそこから外していく判断はちょっとよくわからなかった。CircleCI としては過去のバージョンを digest 使って固定できるし、それを将来の変更に備えるための best practice として紹介しているという言い分はあると思うけど。

それはそうと Java 8 のリリースは2014年と6年も前のことであり、現在の Java の最新は14, 来月には Java 15 がリリースされ、来年2021年には Java 11 の次のLTSである Java 17 がリリースされる予定である*16Java 8 までと Java 9 以降でリリースサイクルが変わったとはいえ、様々なアップデートが行われた、また行われている Java において8はもはやレガシーな環境だと言って良いと思われる。Android OS それ自体は Java そのものを使っているわけではないのでAPIの追従に時間がかかるのは仕方ないことだが、ビルドや local test の実行に関しては Java後方互換性のおかげで結構なんとかなるし*17、現時点で最も困難な (と自分は思っている) Java 9 の壁を越えられるようになったことがわかったので、今後は Android に対して新しいJDKへのサポートを積極的に要求していっても良いのかもしれない。

www.oreilly.com

www.manning.com


Android Gradle Plugin 4.1.0 から library module の BuildConfig に VERSION_CODE と VERSION_NAME が生えなくなりそう (なった)

Android Gradle Plugin 4.0.0 までは library module にも生えていた BuildConfig.VERSION_CODEBuildConfig.VERSION_NAME が 4.1.0-alpha05 から生えなくなってた

issuetracker: https://issuetracker.google.com/issues/154275579

Library module にとっては不要でしょ、とのこと

Version Code mean nothing for Libraries. It's actually potentially confusing as one might expect that the BuildConfig.VERSION_NAME of a library is set to the version name of the app but this was never the case. For these reasons, it's better to not include it in library modules.

AGP 4.1.0-beta01 で試しても同様だった

現在 library module の build.gradleversionCodeversionName を設定して、その module の BuildConfig からバージョン情報にアクセスしているようなアプリはDIでバージョン情報を使うもの/取得できるものを app から配るようにしないとダメそう

追記 (2020-10-13)

4.1.0 stable 出ました

https://developer.android.com/studio/releases/gradle-plugin?buildsystem=ndk-build#4-1-0

記述はないけど上記のままっぽいです 書いてあったわ
アプリの構造や設計によってやりようは色々ですが、大雑把には

  • DI で app module の BuildConfig へのプロキシを配る
  • 自分のアプリの PackageInfo から取得する
  • buildConfigField でバージョン情報を設定する

辺りになるんじゃないでしょうか

CircleCI to GitHub Actions 移行日記

追記 (2020-06-13)

この件その後ログを精査してみたら、メモリの問題じゃなくて実行しているテストの1つにあった非同期処理の実装がまずくて処理が迷子になったまま帰ってこないのが原因だった。

追記終わり

職場の Android アプリのCIに CircleCI を使っていたのだけど、古いプランのままでコンテナのRAMが4GBの medium しか使えないのと、アプリの規模が大きくなりつつあるために色々対策をしてもOOMで落ちることがままあり、幾らかお話をした結果として現状簡素なCIしか無いし欲しいのは十分なRAMくらいなので CircleCI のプランを現行の performance plan にするくらいなら GitHub Actions の方がコスパが良いということで移行をしている。

先にビルドがより不安定な project から移行をして、そちらは問題なく移行できたのだけど、最も安定していた project を移行した際に gradle task の実行中に特に中断されるでもなく応答が返ってこなくなるという挙動に遭遇した。よくあるCI用の実行オプションを足しても特に改善される様子がなかったので debug log を出力させてみたところ、 task の実行途中で空き容量を超えた量のメモリ確保の要求がきて、解放しようとしたが殆どもしくは全く空けられず、延々と request が飛んでくるだけといった状況になってることがわかった。

関連する log は以下のような感じ:

[DEBUG] [org.gradle.process.internal.health.memory.DefaultMemoryManager] 728290099 memory requested, 429182976 free
[DEBUG] [org.gradle.workers.internal.WorkerDaemonExpiration] Will attempt to release 694 of memory
[DEBUG] [org.gradle.process.internal.health.memory.DefaultMemoryManager] 728290099 memory requested, 0 released, 429182976 free
[DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Waiting to acquire shared lock on daemon addresses registry.
[DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Lock acquired on daemon addresses registry.
[DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Releasing lock on daemon addresses registry.
[DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Waiting to acquire shared lock on daemon addresses registry.
[DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Lock acquired on daemon addresses registry.
[DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Releasing lock on daemon addresses registry.
[DEBUG] [org.gradle.process.internal.health.memory.DefaultMemoryManager] 728290099 memory requested, 429182976 free
... (以下繰り返し)

Gradle task 実行時のオプションは以下の通り:

 -Dorg.gradle.jvmargs="-Xmx5120m -XX:+HeapDumpOnOutOfMemoryError" -Dorg.gradle.workers.max=2 -Dorg.gradle.daemon=false -Dkotlin.incremental=false

要求されるメモリの量は常に同じなのでずっと同じ要求が繰り返されているんだろうなと思うのだけど、何のために700MB程度確保しようとしているのか、何故できてもほんの少ししか解放できないのかなどわからないことばかりだし、じゃあアプリの規模に対して -Xmx が小さすぎるのかというと見ての通り少なくない、というかむしろかなり多い方だし、大きくなりつつあるとはいえ特別大規模なアプリではないのでカツカツということはないと思っているので、今の自分の gradle に対する知識ではお手上げだなあとなっている。あとこの gradle task が進まなくなる挙動は必ず発生するわけではなく時々完走するので、何か変更を入れたときにたまたまビルドが通るとそこに原因を求めてしまいがちで調査がなかなか進まずもどかしい。

Groupie に View Binding サポートが追加された

先月出してた groupie-viewbinding を追加する pull request が今日 merge された。

github.com

早速これの入った v2.8.0 のリリースもされた模様。

Release v2.8.0 · lisawray/groupie · GitHub

ものとしては groupie-databinding ほとんどそのままという感じで、型パラメータが ViewBinding になっているのと、View Binding には DataBindingUtil#bind みたいな任意の binding class のインスタンスを生成する方法がないのでそれについては BindableItem#initializeViewBinding を override して自分で書いてね、というところがAPI上の差異になる。

簡単な例としては以下のような感じ:

class MyItem(private val data: MyData) : BindableItem<MyItemBinding>() {

  override fun getLayout(): Int = R.layout.my_item

  override fun initializeViewBinding(view: View): MyItemBinding =
      MyItemBinding.bind(view)

  override fun bind(viewBinding: MyItemBinding, position: Int) {
    // ...
  }
}

また、プロジェクトのAGPが3.6.0以上だと、 以前ブログで書いた通り ViewDataBindingViewBinding を implement するようになったので Data Binding も扱うことができ、 Data Binding と View Binding 両方使う場合は groupie-viewbinding だけ依存を追加すればよくなっている。

ただこれまでに groupie-databinding を使ったことがある人には groupie-viewbinding で Data Binding を扱う時にちょっと注意点があって、groupie-databindingBindableItem は ViewHolder が bind される時に内部で ViewDataBiding#executePendingBindings を実行してくれていたのだけど、 groupie-viewbinding のはそれがなくなっている。なので binding class に対応する layout が持つ変数や observable object を変更することで view の更新を行う場合には、 override する bind の実装の中で自分で executePendingBindings を実行するのを忘れないようにしなければならない。 groupie-databinding から groupie-viewbinding に移行する場合は既存の BindableItem を継承した class 達にはすべて initializeViewBinding の override をして回る必要があるので、その時に確認を忘れないようにしてほしい。

developer.android.com

stackoverflow.com

あと、今回の groupie-viewbinding の追加で groupie-databinding が deprecated になった。近い内にメンテも止める予定だそうだ。まあ確かに groupie-viewbinding が Data Binding も扱えるのでそっちに寄せてしまおうというのはわかるのだけど、新規はともかく groupie-databinding からの移行となるとユーザにはやや不便を強いることになり、その点については心残りがある。

まとめ

  • View Binding サポートの groupie-viewbinding が追加された。Data Binding とも使える
  • groupie-databinding が deprecated になった。メンテも止まる予定
  • (groupie-databinding から移行するユーザ向け) groupie-viewbinding で Data Binding を扱う場合の変更ポイント:
    • Binding class のインスタンス生成のために initializeViewBinding が必要
    • bind の実装の中で layout に持たせた変数や observable objects を経由して view の更新を行っている場合は、最後に executePendingBindings を実行する

******

From: CyberAgent (2019/03~2020/03)
To: U-NEXT (2020/03~)

出戻りです。

CyberAgent では Android アプリの開発をしたり Bitrise で CI を良い感じにしたり GitHub Actions で CI を良い感じにしたりしていました。U-NEXTでも引き続き Android アプリの開発をしたり CI を良い感じにしたりすると思われます。