2022年のAndroidプロジェクトセットアップ

Qiita Nightの資料になります。　 ↑ **右下の拡大ボタンを押すと読みやすくなります。**
https://increments.connpass.com/event/251564/

---

# どうAndroidプロジェクトをセットアップするか？

takahiromという名前で活動しています。
Androidが好きです。

GDE for AndroidやDroidKaigi Co-organizerやCyberAgent Developers Expertなどの活動をしています。

初めてQiitaスライドを使いました。

---

## 初めてAndroidアプリを作りたいという方

Googleから公式のチュートリアルが提供されているので、最新のAndroidの開発について確認してみてください！一緒に開発していきましょう🙌
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27388/e1b85c3b-63b1-0741-e5b9-42e9f7cb6c77.png width=300 />
 https://developer.android.com/courses/android-basics-compose/course

---

## 今日のテーマ: Androidのアプリを複数人で開発して運用していくときに、どうプロジェクトをセットアップするか。

* すべてのサイズのプロジェクトにマッチするセットアップはないと思っていますが、数人ぐらいの規模で考えています。
※ 自分だったらこれを選ぶというだけで、もちろん、この仕組み以外を使っても全然いいと思っています。
※ 何かあれば編集リクエストやコメントなどをください

Googleから[Now in Android](https://github.com/android/nowinandroid)というAndroid関連のニュースアプリが公開されていてかなりいいサンプルのアプリになっています。このアプリの考え方を取り入れています。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27388/cdd4b73c-d7f4-4c08-53e5-cf21712a8134.png width=200 />

---

## 今回の紹介するテンプレートプロジェクト

セットアップした内容をGitHubにあげています。これはテンプレートとして使えます。このプロジェクトの中のコードを参照しながら説明していきます。

https://github.com/takahirom/android-project-template-2022

このサンプルではコードに関しては全然書いておらず、最低限になっていて、cloneして好きなようにコードを書き始められるようになっています。

---

# ビルドシステム

ビルドシステムとしてはGradleを採用を採用します。Androidアプリのプロジェクトのデフォルトの構成なのでGradleを採用することに特に悩む必要はないのかなと思います。
使い方は基本的にAndroid Studioを使うとデフォルトで利用されます。
Bazel(ベイゼル)など別のビルドシステムで頑張ってみるのも楽しいかもしれません。
<details>
Bazelなどにチャレンジするのも良いと思います。現時点ではComposeのLive EditやKotlin Multiplatformなど最新の技術において、やれることが限られる可能性があります。その代わりスケーラブルであったり、ビルド速度が速いなどがあると思いますが、ちょっと現時点では自分はあまり詳しくありません。
</details>

---

# ビルドファイルの書き方
Gradle Kotlin DSL(build.gradle.kts) を採用します。Kotlinでビルド関連のコードが書けます。

* デフォルトのGroovyと比較してKotlinが静的型付けである
* Kotlinなので、Androidエンジニアであれば記述が楽です。

など
使い方は基本的に`build.gradle`のビルドファイルの代わりに`build.gradle.kts`でビルドファイルを書くだけです。

---

## 実際Kotlin DSLってどう？

デメリット

* ビルド速度
* 書き方がbuild.gradleと違うので管理が難しい
* [Kotlin DSL特有の問題など](https://docs.gradle.org/current/userguide/kotlin_dsl.html#kotdsl:limitations)がある場合があるなどが挙げられます。

Kotlin DSLじゃないのも個人的には好きなんですが、最近はKotlin DSLでもかなりサンプルも充実してきた(逆にKotlin DSLしかサンプルがない場合も増えてきた)ので今年はこちらで。

---

# バージョン管理
Gradle Version Catalogでtomlによる管理を採用。
これを利用するとプロジェクト内で、モジュール間やビルドスクリプトでライブラリのバージョンの共有ができます。

使い方はgradle/libs.versions.tomlにtomlファイルを置いて、そこにバージョンを記述します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27388/fd38a579-e58f-78e1-a668-78dd7730d0b2.png)

libs.versions.tomlの例
https://github.com/takahirom/android-project-template-2022/blob/main/gradle/libs.versions.toml

---

### Gradle Version Catalogってどう？1
こちらも採用に是非が分かれるかと思います。Gradle 7.4からPreview機能でもなくなり、Android Open Source ProjectのAndroidXでも採用されたり、Now in Androidでの採用されるなど、ここ一年でかなりメジャーな機能になっています。

---

### Gradle Version Catalogってどう？2


ただ、例えばライブラリを新しく導入する場合には、以下のような文字列がREADMEなどで提供されているのでそれを入れる必要があるのですが、これをVersion Catalogの記法に変換するのがめんどくさいというデメリットがあります。

よくREADMEに書かれているbuild.gradleへの追加の記法

> implementation 'androidx.core:core-ktx:1.7.0'

↓どうにかして以下に直したい。

Gradle Version Catalogの記述例

> androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidxCore" }

---

### Gradle Version Catalogってどう？3

**gradle-version-catalog-converterを作ったので簡単に変換できるので利用してみてください:pray:**
![screencast 2022-07-14 18-32-51.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27388/dc35b11d-3134-5089-afdf-108ee3ea5f78.gif)


https://github.com/takahirom/gradle-version-catalog-converter

---

# 自動バージョンアップ
RenovateがGradle Version Catalogの自動アップデートが使えるのでこちらを使います。
以下から設定すると簡単に利用できます。

https://github.com/renovatebot/renovate#using-renovate

---

## Renovate設定の流れ
* 基本的な導入の流れはWeb上でGitHub Appを追加するボタンが出てくるので、それを押す 
    * https://github.com/apps/renovate
* 適応するプロジェクトが選べるので選ぶ
* Renovateが設定ファイルを追加するプルリクエストを投げるので、それをマージすることで利用開始することができます。

---

## Renovateが作るPR
こんな感じでアップデートのPRを投げてくれます。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27388/ff02fa7e-c568-3e8a-fcc4-e94039b33fcb.png)

---

## Gradleの依存関係の変更の検知

ライブラリを導入したりバージョンアップするときに、ライブラリが推移的にライブラリに依存を始めたり、破壊的変更のあるバージョンへのアップデートをしてしまう場合があります。これをプルリクエストで確認できるようにする方法を紹介します。このようなコメントをプルリクエストにさせることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27388/765c9ef2-b44d-9447-66e9-520d88a0ea70.png)

`JakeWharton/dependency-tree-diff`だとdiffが長くなってしまって、分かりにくいという問題があったので`careem/dependency-diff-tldr`を使っています。


https://github.com/takahirom/android-project-template-2022/pull/23

---

# CIサービス
自分が知る限り、特に新しいトレンドは見つけられていないです。自作も含め、好きなサービスで良いと思いますが、今回はGitHub Actionsを使っています。
GitHub Actionsはファイルを独立して設定できるので移植性が高い(コピペできる)のが個人的に好きなポイントです。またGitHubとの統合は強いなと思います。
ただ、CIを自作したりするともっと柔軟にグラフで確認したり、差分でなにかしたりなどがやりやすい場合があるかもです。

Android StudioのベースとなったIntelliJを作っているJetBrainsが提供しているTeamCityも気になるので、どなたか試して知見を共有してください。

---

# CIで何を行うか

ビルド、テスト、リリース、ベンチマークなどを行うのが良いと思います。基本的にはGitHubを使っていれば、`.github/workflows/xxx.yml`の設定ファイルを置くだけで利用することが出来ます。 Now in Androidで行っていたものがほとんどですがこちらで確認できます。

https://github.com/takahirom/android-project-template-2022/blob/main/.github/workflows/Build.yml

---

# コードフォーマット

フォーマット定義に関してはAndroidのKotlin Style guideを基本的に利用します。これはこの定義によるメリデメというよりはフォーマットが決まっていることによって開発中に議論の手間が減るというメリットがあります。

https://developer.android.com/kotlin/style-guide

---

# コードフォーマットツール CIのセットアップ

KtLint + Spotless。
ローカルではIDE + KtLint + Spotlessを利用します。こちらも別のツールでも良いと思います。

./gradlew spotlessKotlinCheckでチェック
./gradlew spotlessKotlinApplyでコードフォーマットができます。

---

# コードフォーマットツール IDEのセットアップ

* プロジェクト内に`.idea/codeStyles/Project.xml` `.idea/codeStyles/codeStyleConfig.xml`をGitに追加(codeStyleConfig.xmlにProjectの設定使うよ。って書いておいて、Project.xmlの内容が使われるようになるイメージです。)
* gradle.propertiesに`kotlin.code.style=official`を定義

導入PR

https://github.com/takahirom/android-project-template-2022/pull/2/files


---

# コードフォーマットツール補足 1

また、今回は新規プロジェクトなので、利用していませんが、途中から適応する場合に、プロジェクト全体をフォーマットしなくても[例えばmainブランチの状態をベースラインとして、アップデートしていくことができます。](https://github.com/diffplug/spotless/tree/main/plugin-gradle#ratchet)

---

# コードフォーマットツール補足 2
自動でフォーマットをかけてSuggested Changeさせることもできます。 (指摘内容が分かりやすい)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27388/0de584c2-821f-6e2d-9b18-2fb870b05a1b.png)
https://qiita.com/takahirom/items/2a3d2912e74107748765
また最近知ったのですが `.git-blame-ignore-revs` でGitのコミットハッシュを指定すると、フォーマットのコミットをgit blameから除外できるのでおすすめです。

---

# Gradleのモジュール分けについて

Gradleにはプロジェクトという概念があります。
```
root ← ルートプロジェクト
├── :app ← サブプロジェクト, モジュール
└── :library ← サブプロジェクト, モジュール
```
プロジェクトはネストすることができ、基本的には根本にあるルートプロジェクトとサブプロジェクトから構成されます。
サブプロジェクトを慣習的にモジュールと呼びます。

---

# Gradleのモジュール分けについて
モジュールを分けるとさまざまなメリットがあります。

* 並列ビルドやキャッシュによるビルド速度の改善
* コードが分かれることによる並列作業

など

以下にかなりまとまっています。
https://github.com/android/nowinandroid/blob/main/docs/ModularizationLearningJourney.md#overview

---

# どうモジュールを分けるか？

これに関してはさまざまな考え方があると思いますが、Now In Androidアプリでは大きく分けるとappとfeatureとcoreの3モジュールによる分け方を採用しています。 (テンプレートプロジェクトは好きにカスタマイズしてください)

このやり方のメリットはルールが分かりやすいことかもしれません。おそらくcoreはかなり増えていくことになると思います。

* app,feature,coreのモジュールがある
    * app: MainActivityやApplicationクラスなど
    * feature: 特定の機能など
    * core: 共有される必要のあるライブラリモジュール
* app -> feature -> coreの依存方向になっている。
* featureはappモジュールが複数ある場合使い回される可能性がある
* featureがfaetureに依存してはいけない。その場合はcoreモジュールになる。
* coreがcoreに依存しても良い。

---

# ビルドロジックの共通化方法

---

# ビルドロジックの共通化方法
色んな方法を今まで書いてきました。。ベストプラクティスを見つけるのはむずかしい。。

https://qiita.com/takahirom/items/50672e079f42c1b7b00c

https://qiita.com/takahirom/items/0e95d1c1eb6de4f01b0c

https://qiita.com/takahirom/items/f42a22f309b547411975

---

# ビルドロジックの共通化方法

今回はNow in Androidアプリでも採用された、build-logicによるビルドのロジックの共通化を採用しています。
メリデメに関しては https://qiita.com/takahirom/items/f42a22f309b547411975 参照してください。

---

# ビルドロジックの共通化方法

ここで、少し実験的な部分として、**featureモジュールとcoreモジュールにような関係をGradleのロジックの共通化にも導入**しています。
* primitiveプラグイン: 
    * androidのライブラリプラグイン, Compose, Hilt, Spotlessといった単一の機能
* conventionプラグイン: 
    * primitiveプラグインを使ってfeatureなどの一般的なユースケースごとのプラグインを定義する

---

# ビルドロジックの共通化方法 primitiveプラグイン

例えばこのようにcore-uiモジュールのbuild.gradle.ktsは以下のように設定します。

実際にはprimitive同士がComposeはAndroidのプラグインが適応されている必要があるなどの依存関係が必要になるため、その部分はパッケージのパスで表現しています。

```kotlin:core-ui/build.gradle.kts
plugins {
    id("com.example.primitive.android")
    id("com.example.primitive.android.compose")
    id("com.example.primitive.android.hilt")
    id("com.example.primitive.spotless")
}
```

---

# ビルドロジックの共通化方法 conventionプラグイン 1

feature-home/build.gradle.ktsのようなモジュールでは以下のように設定します。ここでは`convention`という名前を採用して、一気に適応できるようにします。

```kotlin:feature-home/build.gradle.kts
plugins {
    id("com.example.convention.androidfeature")
}
```

---

# ビルドロジックの共通化方法 conventionプラグイン 2

そしてそのプラグインの実装では以下のようにそれぞれのプラグインを適応しています。

```kotlin
class AndroidFeature : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            with(pluginManager) {
                apply("com.example.primitive.android")
                apply("com.example.primitive.android.compose")
                apply("com.example.primitive.android.hilt")
                apply("com.example.primitive.spotless")
            }
        }
    }
}
```

---

# ビルドロジックの共通化方法

このようにprimitiveプラグインとconventionプラグインを利用することで構成可能にしつつ、管理できる方法を模索しています。

---

# まとめ

最近の事情を踏まえたAndroidプロジェクトのセットアップをお話しました。
いろんなアプローチがあると思うので、こんなやり方あるよとか教えて下さい。 :pray: 

@ronnnnn 内容のレビューありがとうございました :pray: 

<!--
Tips

`./.github/.githubci-gradle.properties` というのを用意しておいて、CIのはじめに`~/.gradle/gradle.properties`にコピーすることで、CI用の設定ができる

`build-logic/` にもgradle.propertiesを置こう

-->