ChatWork Creator's Note

ビジネスチャット「チャットワーク」のエンジニアとデザイナーのブログです。

Android P Preview SDK でアプリをビルドできるようにしてみよう (Preview 1 時点のお話)

こんにちは @ryugoo_ です。

先日、 Google から Android P Developer Preview が発表されました。残念ながら日本では合法的に Android P Developer Preview をインストールして実機テストできるデバイス (Pixel シリーズ) は発売されていませんが、ひとまずはエミュレーターを使ったテストを行うことができる状態です。 Google には是非とも日本における Pixel シリーズの発売を実現してほしいものです。

それはひとまず横に置いて、 Android P Preview SDK が公開されたことで、私たちチャットワークの Android アプリが Android P Preview SDK を使ってビルドできるかのチェックを行いました。最終的に無事にビルドと動作確認を行うことができましたが、そこに至るまでにハマった過程があります。

今回はハマった事柄と、それをどう解決したのかを共有したいと思います。

ハマったことと解決手段

1. Android Studio の Enter action or option name が機能しない

Android Studio は Android Studio 3.2 Canary 6 を使います。今回テスト行ったマシンは MacBook Pro ですが、 Android Studio 3.0 Stable と Android Studio 3.1 RC がインストールされていて、設定プロファイルは 3.0 Stable のものをインポートしました。

その結果、 macOS 環境においては cmd + a で起動する Enter action or option name ダイアログが機能しなくなりました。ダイアログは表示されるのですが、 Clean Project や Rebuild などの項目が表示されなくなりました。

解決法としては、 File → Invalidate Cache and Restart から Invalidate and Restart を行いました。すると、無事全ての項目が表示されるようになりました。

2. Kotlin Plugin 1.2.30 が使えない

Android Studio 3.2 Canary 5 からテストをしていたのですが、Kotlin Plugin 1.2.21 は使えるものの 1.2.30 がダウンロードできませんでした。

これに対しては Android Studio 3.1 向けの Kotlin Plugin を JetBrains のサイトからダウンロードし、ダウンロードした zip ファイルを展開して生成された Kotlin フォルダを

~/Library/Application Support/AndroidStudioPreview3.2

の中に入れました。その後 Android Studio 3.2 Canary 6 を再起動したところ、無事 Kotlin Plugin 1.2.30 として機能しました。

f:id:cw-miyashita:20180313143413p:plain
Android Studio 3.2 だが、Kotlin Plugin は 3.1 向けのものを使う

3. ビルド時に kapt のプロセスでエラーが出力され、ビルドできない

app/build.gradle を以下のように書き換えました。

android {
-  compileSdkVersion 27
-  buildToolsVersion '27.0.3'
+  compileSdkVersion 'android-P'
+  buildToolsVersion '28.0.0-rc1'
  defaultConfig {
-    minSdkVersion 16
-    targetSdkVersion 27
+    minSdkVersion 'P'
+    targetSdkVersion 'P'
~~~~~
}
ext {
-  support_library_version = '27.1.0'
+  support_library_version = '28.0.0-alpha1'
-  databinding_compiler_version = '3.0.1'
-  databinding_version = '1.3.3'
+  databinding_version = '3.2.0-alpha06'
~~~~~
}
dependencies {
-  kapt "com.android.databinding:compiler:$databinding_compiler_version"
  implementation "com.android.databinding:adapters:$databinding_version"
  implementation "com.android.databinding:library:$databinding_version"
~~~~~
}

これで kapt のプロセスはパスするようになりました。

Kotlin で DataBinding を使う場合、 DataBinding Compiler の使用を kapt で指定する必要がありましたが、これが不要になりました。しかし DataBinding Adapters と Library のバージョンを 3.2.0-alpha06 にしないとビルドすることができませんでした。

4. ビルド時にコンパイルエラーが発生して、ビルドできない

kapt はパスしましたが、今度は Kotlin 側のコンパイルエラーが発生するようになりました。これに関しては Android Studio 側に何が原因なのかが表示されました。

  • DataBindingUtil#bind(View view) が Nullable 型を返すようになり、変数宣言では Non-null 型として Binding を受け取るようにしていた部分で型の不一致が発生
  • Support Library 側で用意された interface / abstract class の Nullable / Non-null 型の変更による型の不一致が発生

と、 DataBinding の生成物と Support Library 側の Nullable / Non-null 型の変更による型の不一致が主な原因でした。 Kotlin では null を許容しない型が求められる場面で null 許容型を使おうとするとコンパイル段階でエラーが出るので、こうした変更の影響を受けます。

とりあえずの解決法としては、DataBinding を行う際にエルビスオペレーターで null だったら IllegalStateException を発生させるようにして対処しました。

val binding: FooViewBinding = DataBindingUtil.bind(view)
    ?: throw IllegalStateException("Can not binding")

これに関しては、前回の私の記事で紹介した拡張関数を使って

inline fun <reified T : ViewDataBinding> View.bindOrThrow(): T =
    DataBindingUtil.bind(this) ?: throw IllegalStateException("Can not binding")

このようなものを用意しておくと、

val binding: FooViewBinding = view.bindOrThrow()

のように書くことができます (refiled / inline を使っているのは私の趣味です) 。

その他、 Support Library 側の Nullable / Non-null の API 変更に対してはチクチクと丁寧に 1 つずつ潰していきました。

4. その他

4-1. Google Play Services

ここまでは Android P Preview SDK + Support Library 28.0.0-alpha1 を使った場合にハマったところですが、もう 1 つ。今回の検証では Google Play Services プラグインのバージョンを 3.1.0 から 3.2.0 にアップデートしたのですが、これが原因でもビルドができなくなりました。

これは Android P Preview SDK などが悪いわけではなく、 Google Play Services プラグイン側の仕様変更によるもののようです。どういう問題が起きるのかというと、 Product Flavors の設定で、Flavor 名の頭文字を大文字の英字にしているとビルドができなくなる というものです。

私たちのアプリではいくつかの Product Flavors を使って開発検証を行っていますが、これの名前の頭文字を大文字の英字にしていたため、ビルドができなくなりました。

Caused by: java.lang.IllegalStateException: No match found

対応方法としては、頭文字を小文字で始める で OK です。

android {
  productFlavors {
    production { // Production → production
    }
    staging { // Staging → staging
    }
  }
}

4-2. ?attr/selectableBackground が参照できない

各 drawable / レイアウトファイルで使用していた

<item android:drawable="?attr/selectableItemBackground"/>
<View android:background="?attr/selectableItemBackground"/>

?attr/selectableItemBackground が Android Studio のコードエディタと Inspection Code を実行時に参照不能のエラーを出力するようになりました。

f:id:cw-miyashita:20180313152250p:plain

ビルドはできるし、機能もするのですが気持ちは良くありません。

とりあえずの解決法として、

<item android:drawable="?android:attr/selectableItemBackground"/>
<View android:background="?android:attr/selectableItemBackground"/>

で逃げの一手を打つことにしました。

4-3. apk をインストールできない

私たちのアプリでは、 apk ファイルをチャットで共有してコードレビュー時に実際に手元で動かしてもらっています。その時に

$ ./gradlew assemble<Flavor Name>Debug

のようにコマンドラインでビルドをしているのですが、これで生成された apk をコマンドライン経由でインストールできなくなりました。私の手元では peco を使って apk ファイルの検索とインストールをできるようにしているのですが、

INSTALL_FAILED_TEST_ONLY: installPackageLI

が出てインストールできませんでした。

これに対しては、 adb install -r に加えて -t オプションを付けて対応しました。

alias installapp='find ./ -name "*.apk" | peco | perl -pe "s/\\(/\\\\(/" | xargs adbp install -r -t'

これが必要な理由は adb の説明ページにキチンと書いてあります。

Android Debug Bridge (adb) | Android Studio

-t: Allow test packages. If the APK is built using a developer preview SDK (if the targetSdkVersion is a letter instead of a number), you must include the -t option with the install command if you are installing a test APK.

おわりに

今回は Android P Preview SDK を使って私たちのアプリをビルドするときにハマったことをまとめました。 Developer Preview は DP5 まで予定されていて、 DP3 で正式 SDK になる予定です。既存のアプリの対応は DP3 からでも良いとは思いますが、 DP1 の段階からどういう対応が必要になりそうなのかを知っておくことは、ユーザーさんへの素早い価値提供につながるかもしれません。

Android 8.0 Oreo 登場後、メーカー製端末も素早く Oreo を搭載してきていましたし、Oreo に入った Project Treble によって P へのアップデート配信が早く始まる可能性もあります。 Pixel がない中で対応を進めるのは難しいですが、それでも精一杯やれることをやっていきましょう💪