みなさん、こんにちは!2022年8月よりAndroidエンジニアとしてインターン中の kitakkun です。
現在MirrativのAndroidチームでは、アプリケーションの品質向上を目指してマルチモジュールの導入を進めています。
Mirrativでは、元々あったmirrorman(当時の開発コードネーム)という巨大なモノリスのモジュールから、新規開発部分をfeatureモジュールに切り出して開発をしています。
元々モノリス部分だったmirrormanが巨大で、mirrormanにあるクラスをfeatureモジュールで使いたいケースがあり、未だmirrormanへの参照が残っている状態です。 なるべくmirrormanへの参照をなくすために、特定のfeatureモジュールでしか使われていないファイルをmirrormanから移動しています。
以後mirrormanを便宜上legacyモジュールと表現します。
リソース整理の課題
legacyモジュールから各featureモジュールにファイルを移動するにあたって抜けがちなのがリソース移動です。 移動するファイルから参照しているリソースの把握が難しく、legacyモジュールにリソースが取り残されてしまいがちです。
現状、ファイル整理がかなり進んできたこともあって、整理できていないdrawableの移動を任されました。
drawableの整理を始めて困ったこと
各解像度のdrawableをまとめて移動できない
Androidでは様々な解像度のデバイスに対応するため、各解像度のdrawableを用意することがあります。移動する際は、各解像度のdrawableをまとめて移動したくなりますが、Android Studioでは一つ一つ移動する以外に手段が用意されていないようです。
特定モジュールで使用しているdrawableの効率的な把握する
IDEで依存を調べるにはFind Usagesを使う他なく、drawableを一つ一つ調べていかなければなりません。
シェルスクリプトを活用してdrawable移動を行う
先の課題があり、人力でdrawableの移動を行うことが難しく、シェルスクリプトを作って効率良く移動することにしました。legacyモジュールにあるdrawableで特定featureモジュールのみが依存するdrawableをリストアップし、移動するシェルスクリプトを作りました。
特定モジュールが依存しているdrawableのリストアップ
まずは、特定モジュールが依存しているdrawableをリストアップします。R.drawable.hoge
と@drawable/hoge
を探します。
R.drawable.hoge
パターンの抽出する
grep
コマンドを用いると、ファイル内に存在する特定文字列を検索することができます。
R.drawable.hoge
パターンは、R\.drawable\.([a-z]|[A-Z]|[0-9]|_)+
で検索できます。
code_dir=$project_root/$module_name/src/main/java grep -srEho "R\.drawable\.([a-z]|[A-Z]|[0-9]|_)+" $code_dir
上記のスクリプトを実行すると R.drawable.hoge
R.drawable.fuga
のような R.drawable
から始まる文字列が抽出されます。
@drawable/hoge
パターンの抽出する
こちらも同様にgrep
コマンドを利用して抽出していきます。
@drawable/hoge
パターンは、@drawable/([a-z]|[A-Z]|[0-9]|_)+
で検索できます。
res_dir=$project_root/$module_name/src/main/res grep -srEho "@drawable/([a-z]|[A-Z]|[0-9]|_)+" $res_dir
上記のスクリプトを実行すると @drawable/hoge
@drawable/fuga
のような @drawable/
から始まる文字列が抽出されます。
特定のモジュールが参照しているdrawableを把握する
特定のモジュールで参照しているdrawableのリソース名を把握するために以下を行います。
- 特定のモジュールで使用されている
R.drawable.hoge
と@drawable/hoge
パターンを抽出する - 抽出した
R.drawable.hoge
と@drawable/hoge
パターンからdrawable名hoge
のみをリストアップ - drawable名から重複を削除する(※R.drawable、@drawableで重複して参照している箇所があるため)
# R.drawableを除いたdrawable名のみを抽出する code_dir=$project_root/$module_name/src/main/java d_in_code=`grep -srEho "R\.drawable\.([a-z]|[A-Z]|[0-9]|_)+" $code_dir` d_in_code=`echo $d_in_code | sed s#R\.drawable\.##g` # R.drawable、@drawable/ を除いたdrawable名のみを抽出する res_dir=$project_root/$module_name/src/main/res d_in_res=`grep -srEho "@drawable/([a-z]|[A-Z]|[0-9]|_)+" $res_dir` d_in_res=`echo $d_in_res | sed s#@drawable/##g` # R.drawable、@drawable/ 両方で参照している場合もあるので重複したdrawable名を削除し出力 printf "%s\n%s\n" ${d_in_code[@]} ${d_in_res[@]} | sort -u
これで特定モジュールが参照しているdrawableを把握することができました。今回はlegacyモジュールにあるdrawableで、特定モジュールのみが依存するdrawableをそのモジュールに移動したいので、他のモジュールから参照がないdrawableを調べる必要があります。
今回の記事では割愛しますが、他のモジュールから参照がないdrawableを調べるためには、例えばfeatureAモジュールの場合、featureAで参照しているdrawableからfeatureB、featureCで依存しているdrawableのリストを除けばfeatureAのみで依存しているdrawableをリストアップできます。 詳しくは完成したシェルスクリプトを記事下部に載せているので参照ください。
特定モジュールのみが依存するdrawableを移動する
find
コマンドを使うと、パターンにマッチする名前のファイルやディレクトリを検索することができます。
さきほど作ったdrawable名から参照しているファイルのフルパスを取得します。
find $project_root/*/src/main/res -name $drawable_name.* -type f -maxdepth 2
上記を実行すると [プロジェクト直下のdir]/module_name/src/main/res/drawable-mdpi/hoge.png
のようなパスが取得できます。
取得したフルパスからファイル名と解像度のディレクトリ名を抽出します。
ファイル名は、パスの終端を取得するbasename
コマンドを使えば簡単です。
また、解像度のディレクトリ名は、フルパスに対してdirname
を使い [プロジェクト直下のdir]/module_name/src/main/res/drawable-mdpi
を取得し、basename
を使います。
取得した情報を用いて移動先パスを構成し、mv
コマンドでファイルを移動します。
# 指定したdrawableを各モジュール内で検索 files=`find $project_root/*/src/main/res -name $drawable_name.* -type f -maxdepth 2` # 移動開始 for file in $files; do file_name=`basename $file` dir_name=`basename \`dirname $file\`` dest=$project_root/$dest_module/src/main/res/$dir_name/$file_name mv $file $dest done
完成したシェルスクリプト
完成したシェルスクリプトはGitHub Gistにて公開しています!
特定モジュールのみが依存しているdrawableを移動するシェルスクリプト
まとめ
マルチモジュール化を進める上で、Android Studioの高度なリファクタ機能は欠かせません。パッケージ名やimport文や参照を自動で修正する機能は、プログラマの負担を大きく軽減してくれます。
しかし、多機能 && 高機能 == 万能
というわけではありません。IDEが提供する標準機能では対応が難しいケースも往々にしてあると思います。そんなときは、シェルスクリプトを活用すると幸せになれるかもしれません。
今回は、Androidプロジェクトでシェルスクリプトを活用し業務効率化したケースを紹介しました。
We are hiring!
ミラティブでは一緒に開発する仲間を募集中です!TwitterのDMなどでお気軽にご連絡ください! www.mirrativ.co.jp
https://speakerdeck.com/hr_team/engineers-handbookspeakerdeck.com