Konifar's WIP

親方!空からどらえもんが!

Androidプロジェクト内の未使用のリソースを削除するgradle pluginを作りました

Androidプロジェクト内で使っていないdrawableやstyle、stringなどのリソースを削除するgradle pluginを作りました。

gradleコマンド一発で完了するので、gradle-pullrequest-builderとCIのジョブを組み合わせれば未使用のリソースを消すPRを自動で作成することもできます。

我ながら便利なので紹介しておこうと思います。

github.com

モチベーション

  • 使っていないリソースが残っているとディレクトリツリーに余計な表示が増えたりコード補完時にノイズになったりするので、定期的に掃除をしていました*1
  • Android StudioのUnused Resourcesを使って検出していたのですが、 DataBindingが登場したあたりからこのツールが嘘をつくようになり、今では全く信用できないものになっていました。また、検出だけではなく実際に消すところも自動でやってくれたらいいのにと思っていました*2
  • 結局grepして本当に使っていないか確認してから消す、というやり方でしばらくやっていたのですが、pythonで簡単なスクリプトを書いたところ、これがとても便利で重宝していました。
  • Androidだけで使うものならgradleで書いた方がいいよなと思っていた時に、DroidKaigi2018でGradleプラグインを作って開発効率を改善しようというセッションがあったのを思い出し、作ってみることにしました。

使い方

導入して、 ./gradlew removeUnusedResources を実行するだけです。

f:id:konifar:20180513121333p:plain

デフォルトでは、以下のファイルをチェックして未使用のファイルやxmlの要素を削除してくれます。 values-v21、strings-untranslatable.xml のようにsuffixがつくファイルも対象です。

|--res
   |--anim
   |  |--*.xml
   |--animator
   |  |--*.xml
   |--drawable*
   |  |--*.xml
   |  |--*.png
   |  |--*.jpg
   |  |--*.9.png // 9-patch
   |--layout*
   |  |--*.xml
   |--menu
   |  |--*.xml
   |--mipmap*
   |  |--*.xml
   |  |--*.png
   |--values*
      |--attrs*.xml
      |--bools*.xml
      |--colors*.xml
      |--dimens*.xml
      |--floats*.xml
      |--ids*.xml
      |--integers*.xml
      |--strings*.xml
      |--styles*.xml
      |--themes*.xml

削除対象にしたくないファイルやディレクトリはgradleファイル内で個別に設定できます。

例えば、drawableファイル名をコード内で動的に組み立てて使っている場合は次のように設定してください。

resources.getIdentifier("img_welcome_${number}", "drawable", context.getPackageName())
unusedResourcesRemover {
  excludeNames = [
    "img_welcome_"
  ]
}

デフォルトのチェック対象にないファイルを設定することもできます。

例えば、text_appearance.xml の中にTextAppearanceのstyleだけをまとめたファイルを作っている場合は次のように設定してください。

unusedResourcesRemover { 
  extraRemovers = [
    createXmlValueRemover("text_appearance", "style", "style", "style")
  ]
}
  • 第一引数はファイルの名前
  • 第二引数は @style/hoge、R.style.hoge のようにチェック対象となる文字列
  • 第三引数は <style > のようにファイル内のタグの名前
  • 第四引数はチェックのパターン。 styledrawablelayout 以外は引数なしOKです。

フィードバックください

一応本業のKyashとDroidKaigi2018と副業先のプロジェクトでいい感じに動いていたのでリリースしましたが、プロジェクトの構成によってはうまく動かないかもしれません*3

実際に、sys1yagiさんのプロジェクトでは相当時間がかかってしまっていたようで、チェック部分のパフォーマンスを改善してくれました :bow:

github.com

何かあれば、気軽にIssueかPRを作ってもらえるとありがたいです!


今回初めてgradle pluginを作りましたが、tnjさんのこの資料はめちゃくちゃよかったです。

speakerdeck.com

基本的な作り方からデバッグ、公開の仕方まで網羅されていて、初めてgradle pluginを作る上ではこの資料だけ見ておけばよいと思います。

*1:gradleのshrinkResourcesとminifyEnabledオプションを使えば、apkに無駄なリソースが入ることはないのでユーザーから見て問題になることはないです。

*2:https://github.com/KeepSafe/android-resource-remover というpythonのツールもありますが、Android Lintをベースにしているので使い物になりませんでした。

*3:モジュールがネストしているパターンはテストできていません