【Android Studio Flamingo】dependencies をバージョンアップデートするときに確認するべき3つの相性

ソフトウェアの開発において、依存関係は重要な要素の一つです。一つのライブラリが別のライブラリに依存している場合、最新バージョンのアップデートを行う際には、注意が必要です。

今回は、依存関係をアップデートする際に留意しておくべきことについて解説します。

で、[File] - [Project Structure] からのこの画面。

This project uses Gradle Version Catalogs. There are some limitations. Learn more.

【Android Studio Flamingo】dependencies をアップデートするときに確認しておくべきこと

[Suggestions] 画面だけで完全に更新できませんよね。

「プラグイン、ライブラリ同士の互換性は考慮されていないものがある。」

「提案されない表示されないライブラリがある。」

ということで、どこに注意しながら新しいものに更新していくとスムーズに進むか、という話です。

 

🔗 Android Studio と Android Gradle Plugin

Android Studio は、Android アプリ開発に必要な開発環境を提供する統合開発環境です。

Android Gradle Plugin (AGP) は、Android Studio のビルドシステムに使用されるツールであり、ビルドプロセスを自動化するために必要です。

Android Studio と Android Gradle Plugin
👉 Android Gradle plugin and Android Studio compatibility - Android Studio Flamingo | 2022.2.1  |  Android Developers hatena-bookmark

Android Studio で使用している Android Gradle Plugin のバージョンを確認するには、プロジェクトの build.gradle ファイルを開き、以下のように dependencies ブロック内に記述されている AGP のバージョン番号を確認します。


dependencies { 
  classpath 'com.android.tools.build:gradle:x.y.z' 
  // ... 
}

または、build.gradle に記述がない Version Catalog 記述の Android Gradle Plugin のバージョンは、以下の、どちらかのプラグインID のバージョン(共通) です。


com.android.application

com.android.library

👉 【Plugin DSL】Android Gradle Plugin のバージョンを調べる方法 hatena-bookmark
👉 【Plugin DSL】「com.android.tools.build:gradle」の記述は不要? hatena-bookmark

Gradle Version Catalogs では以下のようになります。


# libs.versions.toml 

[versions]
android-gradle-plugin = "8.0.1"

[plugins]
android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }


// buid.gradle

plugins {
  alias libs.plugins.android.application
}

また、最新の AGP バージョンを確認するには、Google's Maven Repository を参照します。
Google's Maven Repository
👉 com.android.application.com.android.application.gradle.plugin - Google's Maven Repository hatena-bookmark

 

🔗 Android Gradle Plugin と Gradle

Gradle は、オープンソースのビルドツールであり、Java や Kotlin、Groovy などのプログラミング言語に対応しています。Android Studio のビルドシステムには、Gradle が採用されています。

AGP は、Gradle に依存しており、AGP のバージョンアップに伴い、Gradle のバージョンもアップデートする必要があります。

また、Gradle のバージョンアップには、Android Studio のバージョンアップも伴う場合がありますので、注意が必要です。

Android Gradle Plugin は Gradle に依存しているため、AGP をアップデートする場合には、Gradle も同時にアップデートする必要があります。

Android Gradle Plugin と Gradle
👉 Update Gradle - Android Gradle plugin release notes  |  Android Studio  |  Android Developers hatena-bookmark

Gradle のバージョンを確認するには、プロジェクトの gradle/wrapper/gradle-wrapper.properties ファイルを開き、以下のように distributionUrl に記述されている Gradle のバージョン番号を確認します。


distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip

また、最新の Gradle バージョンを確認するには、Gradle 公式サイトの Gradle Distributions を参照します。
Gradle Distributions
👉 Gradle Distributions hatena-bookmark

 

🔗 Kotlin と Jetpack Compose Compiler

Kotlin は、Android アプリ開発に必要なプログラミング言語の一つです。Jetpack Compose Compiler は、Kotlin をベースにした Jetpack Compose ライブラリのコンパイラです。

Jetpack Compose は、Android のユーザーインターフェースを作成するための新しい方法を提供するライブラリであり、Kotlin と密接に関連しています。

https://developer.android.com/jetpack/androidx/releases/compose-kotlin#pre-release_kotlin_compatibility
👉 Compose to Kotlin Compatibility Map  |  Android Developers hatena-bookmark

Jetpack Compose Compiler のバージョンをアップデートする場合には、Kotlin のバージョンも確認し、必要に応じてアップデートする必要があります。

Kotlin と Jetpack Compose Compiler の依存関係を確認するには、以下のように Kotlin と Compose Compiler のバージョン番号を確認します。


# libs.versions.toml 

[versions]
compose-compiler = "1.4.7"
kotlin = "1.8.21"

[plugins]
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }


// build.gradle

plugins {
  alias libs.plugins.kotlin.android  // 'kotlin-android'
  alias libs.plugins.kotlin.kapt     // 'kotlin-kapt'
}

android {
  composeOptions {
    kotlinCompilerExtensionVersion libs.versions.compose.compiler.get()
  }
}

また、最新の Kotlin と Jetpack Compose Compiler のバージョンを確認するには、それぞれ以下を参照します。

👉 Maven Repository: org.jetbrains.kotlin » kotlin-gradle-plugin hatena-bookmark
👉 androidx.compose.compiler.compiler - Google's Maven Repository hatena-bookmark

 

🔗 まとめ

Android Studio のビルドシステムには、Android Gradle Plugin が使用されており、AGP は Gradle に依存しています。

また、Kotlin と Jetpack Compose Compiler、そして SDK のライブラリ同士にも依存関係があります。

最新バージョンにアップデートする際には、それらのつながりを確認し、必要に応じて一括でアップデートすることをおすすめします。

コアな部分は Stable 最新バージョンで揃える、とすると、以下が、今現在ベストでしょう。


Android Studio
Flamingo | 2022.2.1

  ↕︎

Android Gradle Plugin
8.0.1

  ↕︎

Gradle
8.1.1


Kotlin
1.8.21

  ↕︎

Jetpack Compose Compiler
1.4.7

すぐ忘れるので libs.versions.toml にコメントで貼りつける。


[versions]
# Android Studio - AGP - Gradle
# Kotlin - Compose Compiler
#
# Android Studio version	Required plugin version
# Giraffe | 2022.3.1	3.2-8.1
# Flamingo | 2022.2.1	3.2-8.0
# https://developer.android.com/studio/releases#android_gradle_plugin_and_android_studio_compatibility
#
# Plugin version	Minimum required Gradle version
# 8.2	8.1
# 8.1	8.0
# 8.0	8.0
# https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
#
# Android Studio :
# https://developer.android.com/studio
#
# Android Gradle Plugin :
# https://maven.google.com/web/index.html#com.android.application:com.android.application.gradle.plugin
#
# Gradle :
# https://services.gradle.org/distributions/
android-gradle-plugin = "8.0.1"

# Compose Compiler Version	Compatible Kotlin Version
# 1.4.7	1.8.21
# 1.4.6	1.8.20
# 1.4.5	1.8.20
# 1.4.4	1.8.10
# https://developer.android.com/jetpack/androidx/releases/compose-kotlin#pre-release_kotlin_compatibility
#
# compose-compiler :
# https://maven.google.com/web/index.html?authuser=0#androidx.compose.compiler:compiler
#
# kotlin :
# https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-gradle-plugin
# https://central.sonatype.com/artifact/org.jetbrains.kotlin/kotlin-gradle-plugin/
compose-compiler = "1.4.7"
kotlin = "1.8.21"

コメント内URLからリンクをブラウザを開くのは、⌘ (command) + click で。



【Retrofit】データクラス エンティティ フィールド名の付け替え @SerializedName @Json @SerialName

同じ Retrofit を使うにしても、パーサーによってアノテーション記述が変わるので、ググると無駄に時間がかかってしまう。

【Retrofit】データクラス エンティティ フィールド名の付け替え @SerializedName @Json @SerialName

 

🔄 Gson


data class User (
    @SerializedName("id")
    long userId;
    @SerializedName("fullName")
    String name;
)

👉 gson/UserGuide.md at master · google/gson hatena-bookmark

 

🔄 Moshi


@JsonClass(generateAdapter = true)
data class User (
    @Json(name = "id")
    long userId;
    @@Json(name = "fullName")
    String name;
)

👉 square/moshi: A modern JSON library for Kotlin and Java. hatena-bookmark

 

🔄 kotlinx.serialization


@Serializable
data class User (
    @SerialName("id")
    long userId;
    @SerialName("fullName")
    String name;
)

👉 kotlinx.serialization/basic-serialization.md at master · Kotlin/kotlinx.serialization hatena-bookmark

 

🔄 まとめ

初めて使うときは混乱するだろうと思います。

今となっては、JSONパーサーは、Kotlin 内蔵の kotlinx.serialization を使いたいですね。

👉 「Kotlinx Json」の登場でサードパーティJSONライブラリは不要となる。 hatena-bookmark


テストの「Mock(モック)」についての著名ソフトウエアエンジニアはどう思っているか🤔

職場や開発環境や対象となるサービスによっては、テストが「だるすぎ」と思えることがあります。

世界的に著名な方々の意見が気になりますよね。

どのように心得て書くべきか、というようなことをまとめてくれています。

「Mock」についてです。

👉 Unit Testing Experts on Mocks | Perry Street Software Engineering hatena-bookmark

 

👨‍💻 Martin Fowler

In classic tests, many people like the fact that client tests may catch errors that the main tests for an object may have missed, particularly probing areas where classes interact. Mockist tests lose that quality. In addition you also run the risk that expectations on mockist tests can be incorrect, resulting in unit tests that run green but mask inherent errors

When you write a mockist test, you are testing the outbound calls of the SUT to ensure it talks properly to its suppliers. A classic test only cares about the final state — not how that state was derived. Mockist tests are thus more coupled to the implementation of a method. Coupling to the implementation also interferes with refactoring, since implementation changes are much more likely to break tests than with classic testing.

I don’t see any compelling benefits for mockist TDD, and am concerned about the consequences of coupling tests to implementation. I really like the fact that while writing the test you focus on the result of the behavior, not how it’s done. A mockist is constantly thinking about how the SUT is going to be implemented in order to write the expectations. This feels really unnatural to me.

  • クライアントテストがオブジェクトのメインテストでは見逃す可能性があるエラーをキャッチできる。
  • クラス同士がやりとりする領域を調査することができる。
  • 期待値が誤っている場合、単体テストが通過し、本来のエラーが隠蔽される可能性がある。
  • メソッドの実装により結合しやすいため、クラシックテストよりもテストが破綻する可能性が高くなる。

👉 Mocks Aren't Stubs hatena-bookmark

 

👨‍💻 Robert C. Martin (Uncle Bob)

Mocking the interactions between all classes forces you to create mocks that return other mocks (that might return yet other mocks). You have to mock out all the data pathways in the interaction; and that can be a complex task. This creates two problems. 1. The setup code can get extremely complicated. 2. The mocking structure become tightly coupled to implementation details causing many tests to break when those details are modified.

The need to mock every class interaction forces an explosion of polymorphic interfaces. In statically typed languages like Java, that means the creation of lots of extra interface classes whose sole purpose is to allow mocking. This is over-abstraction and the dreaded ‘design damage’.

I recommend that you mock sparingly. Find a way to test — design a way to test — your code so that it doesn’t require a mock.

  • インタラクションをシミュレートするための便利なツール。
  • クラス同士のインタラクションをすべてモックする必要があるため、セットアップコードが複雑になる。
  • モックの構造は実装の詳細に密接に結合するため、実装の変更により多くのテストが破綻する可能性がある。
  • ポリモーフィックなインターフェースが必要となるため、設計上の過剰な抽象化が生じる可能性がある。

👉 Clean Coder Blog hatena-bookmark

 

👨‍💻 Kent Beck

I mock almost nothing. If I can’t figure out how to test efficiently with the real stuff, I find another way of creating a feedback loop for myself.

If I use TDD, I can refactor stuff. And then I heard these stories people say that they use TDD and now they can’t refactor anything. I couldn’t understand that and then I started looking at their tests: If you have mocks returning mocks, returning mocks, your test is completely coupled to the implementation, not the interface, but the exact implementation. Of course you can’t change anything without breaking the test. That for me is too high a price to pay, that’s not a trade-off I’m willing to make.

  • ほとんど使用しない。
  • 多重に入り組んでいると、テストが実装に結合しすぎるため、実装の変更が難しくなる。

👉 TW Hangouts | Is TDD dead? - YouTube hatena-bookmark

 

👨‍💻 Ian Cooper

When we refactored something all these tests with mocks broke and we had to rewrite all the mocks. It was kind of puzzling because the promise was that we should be able to refactor our code, because our tests would enable us to refactor and refactoring kept our code healthy. But the tests were an obstacle to that change, since they were breaking when we changed our implementation details.

Avoid heavy mocking. This allows you to meet the promise to refactoring. You will refactor your code and your tests won’t break.

Mocks are useful when a resource is expensive to create, but the problem with mock objects essentially is that people have used them to isolate classes. Don’t do that.

  • リソースを作成するのがコストが高い場合に便利である。
  • オブジェクトを使用すると、クラスを分離するために使用された場合、リファクタリングが非常に困難になる可能性がある。
  • ヘビーなモックは避けるべきである。

👉 🚀 TDD, Where Did It All Go Wrong (Ian Cooper) - YouTube hatena-bookmark

 

👨‍💻 まとめ

大まかにまとめると以下の3つ。

Mock はインタラクション(やり取り/相互作用)の確認が目的である。

リソース作成のコストが高い場合に便利。

実装と密になるので、実装は変更はしづらくなっていく。

ホンマもんのエンジニアの方々に口を揃えて言われるとスッキリします。