Mirrativ Tech Blog

株式会社ミラティブの開発者(バックエンド,iOS,Android,Unity,機械学習,インフラ, etc.)によるブログです

マルチモジュールでのリソース整理

みなさん、こんにちは!2022年8月よりAndroidエンジニアとしてインターン中の kitakkun です。

現在MirrativのAndroidチームでは、アプリケーションの品質向上を目指してマルチモジュールの導入を進めています。

Mirrativでは、元々あったmirrorman(当時の開発コードネーム)という巨大なモノリスのモジュールから、新規開発部分をfeatureモジュールに切り出して開発をしています。

モノリスモジュールから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