Android プロジェクトの未使用リソースを一括で削除する Gradle Plugin を公開しました。このプラグインのメリットや使い方を解説します。
2023/08/08: v2.1.0 に合わせて更新
2023/08/05: Remove Unused Resources Plugin for Android v2.0.0 の内容に更新しました
Remove Unused Resources Plugin for Android
Remove Unused Resources Plugin により、Gradle Task から Android の未使用リソースを削除できるようになります。
特徴やメリット
- Android Lint
UnusedResources
ルールの実行結果lint-results.xml
をもとにリソースの削除を実行します- 未使用リソースの検出は Android Lint に任せているため、Android Studio の
Refactor > Remove Unused Resources...
メニューと同じ結果を期待できます
- 未使用リソースの検出は Android Lint に任せているため、Android Studio の
- マルチモジュールプロジェクト対応
- Android Lint がマルチモジュールプロジェクトでのリソース検出に対応しています
- DataBinding, Epoxy 対応
- Android Lint がコード生成解析 (checkGeneratedSources) に対応しているため、正しく解析できます
- Github Actions などの CI での実行に適しています
- Gradle Task であるためどこでも実行できます
- Android リソースのすべての形式に対応しています
- Resource Type のすべてに対応しています
- R.string などの XML tag の削除も、R.layout などのリソースファイル削除も対応しています
- drawable-{density} や values-v26 などの Alternative リソースも正しく検出します
- 9-patch ファイルも正しく検出します
- もとの XML をなるべく維持する
- XML 内のリソースを削除するときに、もとの XML のインデントやスペースを維持します
-
&#{unicode}
などの文字列参照なども元の XML の表現をそのまま残して処理します
- 高速動作
- ファイルや XML tag の削除以外の無駄な処理はしていません
- Android Lint タスクで未使用リソースを検出する仕組みのため、実行時間のほとんどは Android Lint に割かれると思います
前提
Android マルチモジュールで Gradle Kotlin Script (*.gradle.kts) 構成のプロジェクトを前提として説明します。
Gradle や Android Gradle Plugin のバージョンなど必要要件は README.md を参照してください。
使い方
このプラグインでは新しい Gradle Plugin Management に対応しています。settings.gradle.kts へ以下の設定を追加します。
settings.gradle.kts
// ...
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal() // Remove Unused Resources Plugin は Gradle Plugin Portal へ登録されています
}
}
// ...
Android アプリモジュールへ Remove Unused Resources Plugin を適用します。
app/build.gradle.kts
plugins {
id("io.github.irgaly.remove-unused-resources") version "{最新バージョンを指定}"
}
DataBinding や Epoxy などのコード生成を使っている場合は、生成コードも解析対象に含めるために、コード生成をしているモジュールごとに checkGeneratedSources を設定します。
app/build.gradle.kts
android {
lint {
// コード生成を使っているモジュールでは checkGeneratedSources を有効にする
checkGeneratedSources = true
}
}
othermodule/build.gradle.kts
android {
lint {
// コード生成を使っているモジュールでは checkGeneratedSources を有効にする
// rur.lint.onlyUnusedResources オプションを使用する場合はモジュール個別の定義は不要になります
checkGeneratedSources = true
}
}
これで Remove Unused Resources Plugin を実行する準備は整いました。
Android Lint を実行して UnusedResource
を検出し、app/build/reports/lint-results-{variant}.xml
を生成します。
ここでは variant = debug とします。
% ./gradlew :app:lintDebug
Android Lint の結果にもとづいてリソースを削除します。削除されるリソースを確認するために Dry Run で実行してみます。Dry Run モードでは削除対象を列挙するだけで実際には削除を実行しません。
(オプションの rur
は Remove Unused Resources の頭文字です)
% ./gradlew :app:removeUnusedResourcesDebug -Prur.dryRun
...
> Task :app:removeUnusedResourcesDebug
> Task :app:removeUnusedResourcesDebug
> report from: /src/project/app/src/main/res/values/colors.xml
[dry run] delete resource element: R.color.black
[dry run] delete resource element: R.color.unused_color
[dry run] delete resource element: R.color.unused_color_with_night_theme
skip because it has tools:override: R.color.unused_override
> report from: /src/project/app/src/main/res/values/strings.xml
[dry run] delete resource element: R.string.unused_string
> report from: /src/project/app/src/main/res/values-night/colors.xml
[dry run] delete resource element: R.color.unused_color_with_night_theme
[dry run] delete resource file because of empty: /src/project/app/src/main/res/values-night/colors.xml
...
結果を確認して問題なさそうであれば -Prur.dryRun
引数を消して実際に削除を実行します。
% ./gradlew :app:removeUnusedResourcesDebug
...
> Task :app:removeUnusedResourcesDebug
> report from: /src/project/app/src/main/res/values/colors.xml
delete resource element: R.color.black
delete resource element: R.color.unused_color
delete resource element: R.color.unused_color_with_night_theme
skip because it has tools:override: R.color.unused_override
> report from: /src/project/app/src/main/res/values/strings.xml
delete resource element: R.string.unused_string
> report from: /src/project/app/src/main/res/values-night/colors.xml
delete resource element: R.color.unused_color_with_night_theme
delete resource file because of empty: /src/project/app/src/main/res/values-night/colors.xml
...
未使用のリソースファイルや、未使用リソースの XML tag が削除されました。
たとえば、values 以下のファイルの XML tag は以下のように削除されます。
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -6,8 +6,6 @@
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
- <color name="unused_color">#FFFFFF</color>
- <color name="unused_color_with_night_theme">#FFFFFF</color>
<color name="unused_exclude_color">#FFFFFF</color>
<color name="unused_exclude_pattern_color">#FFFFFF</color>
</resources>
UnusedResources
ルールだけの Android Lint を実行する
Android Lint をそのまま実行すると UnusedResources
以外の Lint も実行してしまうため時間がかかってしまいます。
未使用リソースを検出するだけでよければ UnusedResources
ルールだけのチェックをすると効率的です。
Remove Unused Resources Plugin を適用していれば以下のオプションを指定することで UnusedResources
ルールだけをチェックすることができます。
% ./gradlew :app:lintDebug -Prur.lint.onlyUnusedResources
このオプションを指定すると、以下の設定で lintOptions を上書きすることになります。
android {
lint {
// -Prur.lint.onlyUnusedResources オプションを付けると、以下の設定が自動で適用されます
xmlReport = true // app project のみ
checkDependencies = true // app project のみ
checkGeneratedSources = true
checkOnly.clear()
checkOnly.add("UnusedResources")
warning.add("UnusedResources")
}
}
オプションについて、詳しくは README.md を参照してください。
CI からリソース削除を実行する
Github Actions などの CI 環境で自動的に不要リソースを削除し、不要リソース削除の PR や commit を生成するようにすると便利です。
CI で実行する場合はたとえば以下のコマンドのようになります。
% ./gradlew :app:lintDebug -Prur.lint.onlyUnusedResources
% ./gradlew :app:removeUnusedResourcesDebug
このコマンドは以下の処理を実行します。
-
UnusedResources
ルールのみで Android Lint を実行する - Android Lint の結果
app/build/reports/lint-results-debug.xml
に従って、未使用リソースを削除する
リソース削除の除外設定
Android Lint で検出されても削除したくないリソースを設定しておくことができます。
app/build.gradle.kts
removeUnusedResources {
// 文字列 "R.{type}.{resource name}" との完全一致で除外リソースを指定する
excludeIds.add("R.color.unused_exclude_color")
// 文字列 "R.{type}.{resource name}" に対して正規表現(完全一致)で除外リソースを指定する
excludeIdPatterns.add("R\\..*exclude_pattern.*")
// 削除対象のリソースファイルに対して File glob 形式で、除外リソースを指定する
// ここで指定したファイルやファイル内の XML tag は削除対象から除外されます
excludeFilePatterns.add("**/values/exclude_colors.xml")
}
設定について、詳しくは README.md を参照してください。