月: 2022年8月

Upgrade AssistantでAndroidGradlePluginとGradleをアップデートする

今回はAndroidGradlePluginとGradleのアップグレードを行います。以前は「AGPとGradleのバージョンを変更」→「Sync Project with Gradle Files」→「エラーやワーニングの指摘箇所を修正」→「Sync Project with Gradle Files」→…と何度も確認する必要があったりして面倒でしたが、AndroidStudioにUpgradeAssistant機能が追加されたので簡単にアップグレードできるようになりました。

対象

  • AGPとGradleをアップデートしたい方

今回は試しにAGP=4.2.2→7.2.1、Gradle=6.9.2→7.3.3、マルチモジュール環境、KotlinDSL(build.gradle.kts)済み、buildSrcでライブラリのバージョン管理をしているプロジェクトに対してUpgradeAssistantでアップグレードしてみます。

AndroidでSync Project with Gradle Filesを実行すると下記のようなポップアップが出ることがあります。

この表示が出ているときはAndroidGradlePlugin(以下AGP)とGradleのアップグレードが推奨されています。upgradedの部分を押すとUpgradeAssistantが表示されます。ポップアップが表示されない場合はShiftボタンを2回押してUpgrade Assistantと入力するとUpgradeAssistantを起動できます。

このままだと「Cannot find AGP version in build files.」と表示されアップグレードできません。これはルートのbuild.gradle.ktsにAGPが直接書かれておらず、buildSrc等を使って別のファイルにAGPのバージョンが書かれている場合に発生します。これを修正する場合は一旦AGPをルートのbuild.gradle.ktsに直接書き、アップグレードが完了したら元に戻しましょう。

このように記述するとUpgradeAssistantがAGPのバージョンを認識できるようになったのでSync Project with Gradle Filesを実行します。すると下記のようにエラーが無くなりRun selected stepsボタンが押せるようになります。

更に左側のRecommended post-upgrade stepsとRecommended stepsの全てにチェックを入れてRun selected stepsを実行します。マルチモジュールの場合はモジュール毎のbuild.gradle.ktsも全て書き換えてくれます。

先程書き換えたルートのbuild.gradle.ktsに書かれているAGPの宣言を元に戻してbuildSrcに書かれているAGPのバージョンを7.2.1にすれば完了です。あとはアプリを実行して動作確認しましょう。


MoshiでJsonデシリアライズするとCould not compute caller for function: public constructor クラス名と表示されてクラッシュする

APIからJsonを取得しデシリアライズするときは以前はGsonが使われていましたが最近はMoshiが使われています。アプリの本番ビルドをしたときは難読化の影響を受けるのでMoshiでデシリアライズする場合はそれを考慮に入れないとクラッシュしてしまいます。その場合はproguard-rules.proに下記のようにルールを追加します。

-keepclassmembers class ファイルが存在するパッケージ名.** { *; } // **にデシリアライズするときのモデルのファイルがある

proguard-rules.proの書き方や他のオプションもあるのですが、とりあえずクラッシュだけさせない場合は上記のみ書けばクラッシュしなくなります。


enum classとwhenを合わせて使う

以前にsealed class とwhenで条件分岐を行うとラクだよという記事を書きました。sealed interfaceでも同様です。

これに似た作用を得る方法にenum classも使えます。

/**
 * You can edit, run, and share this code.
 * play.kotlinlang.org
 */
enum class Direction {
  NORTH, SOUTH, WEST, EAST
}
fun main() {
    val direction = Direction.SOUTH
    val num = direction.ordinal
    when (direction) {
        Direction.NORTH -> print("north : " + num)
        Direction.SOUTH -> print("south : " + num)
        Direction.WEST -> print("west : " + num)
        Direction.EAST -> print("east : " + num)
        else -> print("other" + num) //'when' is exhaustive so 'else' is redundant here
    }
}

https://pl.kotl.in/qPkIZ9hw0

これを実行するとelseの左側に「’when’ is exhaustive so ‘else’ is redundant here」と表示され、else文には絶対条件分岐しないので、書かなくて良いよと教えてくれます。ドメインモデルを書くときは積極的に使うとコンパイル時に間違いに気付くことができます。enum classの利点なはordinalを自動で付与してくれることなので、大半の場合はsealed classやsealed interfaceを使うことをが多いと思います。