大規模なAndroidアプリコードを読み解く技術

# はじめに ~`XXXFragment`に飛びたいだけなのに~
コードが大規模化すると，どこに何が書いてあるのかを追うのが難しくなる傾向にあります．Androidでは

- `.java`と`.kt`が混ざっていたり，([JNI](https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/spec/jniTOC.html)等によってNativeなコードが呼び出されていたり)
- 画面を構築するためのxmlファイル等の`res/`以下にあるようなリソースファイルがあったり，
- [databinding](https://developer.android.com/topic/libraries/data-binding?hl=ja)や[Dagger2](https://dagger.dev/)等のリフレクションによるコードの自動生成に依存する箇所があったり

と，考慮することが少々多いような気もします(気がするだけかもしれません)


この画面を作成している`XXXFragment`に飛びたいだけなのに....どこにそれがあるの？？なんてことも起こりえます．**特にインターンシップのような短い期間に急速にコードをキャッチアップして成果をアウトプットする必要がある場合**には，コードの『読解力』が重要になってきます．

今回の記事では私自身がこのような問題に対してどうやって対処しているか？また，読みやすいコードを作っていくにはどうするべきなのか？といった広い課題に対して記事を書いていこうと思います．

## アウトライン
以下の流れで書いていきます．

- 1. 大規模化したコードにストレスなく対応できる環境つくり編
- 2. 読み解くためのフロー・tips集編
- 3. 読みやすいコードにするためにリファクタする編

# 1. 大規模化したコードにストレスなく対応できる環境つくり編
ここでは小さくtips的に紹介したいと思います．全部やればいいというよりは自分の環境に使えそうなものだけピックアップして適用すると良さそう:eyes:

## AndroidStudioの設定
### ASのヒープ領域を増やす
こんなpop upが出てきたことありませんか？せっかく多くのメモリを積んでいるマシーンでもリソースを無駄にしてしまう可能性があります．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176701/4d756d25-7391-150c-25c2-aae733f1646e.png)

- 使える領域が余ってそうならガンガン増やしてみる → [参考](https://developer.android.com/studio/intro/studio-config?hl=ja)

雑に設定するならAppearance & Behavior > SystemSettings > Memory Settingsをシュッとすれば良さそう:eyes:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176701/2c791097-f8c7-3455-fa59-3b166ae4e827.png)


### 文字サイズや行間を小さくする
デフォルトはでかいので小さくします．小さくすると一度に脳が処理できる情報が増え，全体を俯瞰的に見るのに適していると思います．また，個人的には行間も狭くするほうが好きです．

1. [Command] + , でpreferencesを開く．
2. Search欄で"font"と検索してそれっぽい項目を選択する．
3. 下の確認欄を見ながら大きさを調整する．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176701/3f9c3253-5f2f-29bf-f8d0-10970faaa691.png)

### タブが消えないようにする
私はどの位置にどのファイルを開いていたかなんとなく覚えていてその記憶を元にファイルをオープンすることがあるので，タブがone rowで表示されないような設定にしています．当然タブの履歴は縦方向に積まれていきますが，相当多く開きすぎない限りは問題ありません．

特に多くのファイルを行ったり来たりする事を想定しているので，いちいち消えられると変に過去に開いたファイルを探す必要があるので無駄足です．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176701/b3e94b10-44d3-a601-eb96-13cf9e7e493c.png)

設定方法はShow tabs in one rowをuncheckすることです．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176701/709c153a-4b86-20c7-3c8f-652682ed5932.png)


### ショートカットキーを覚える
よく使う`[shift]` `[shift]`を始め，`[command]` + `[`, `]`，`[command]` + `[shift]` + `f`などたくさんあります．これらを使いこなすと様々な操作が楽になるので，ぜひ使えるようになりましょう！！！以下参考資料．

[Android Studio30分集中超絶技巧100選メモ](https://gist.github.com/yusuke/e30257656a78ca007627e0762a5a7fbc)

### ショートカットキーを自分好みにする
大体はデフォルトで良さそうな感じがするんですが，`Find Usages`とか呼び出しの関係を確認するときによく使うのにデフォルトだと変なやつがあったりします．そういうやつは変えて**も**いいかもしれませんが，他人が触れないASが出来上がってしまうのでやりすぎるのは良くない気がしてます．

## 外部ビルド環境の構築
大規模化したコードはノートPCでは

   - 鬼のようにビルド時間がかかる
   - ビルド時にASを触るともっさりしている．
   - ビルドにマシンパワーを割きすぎてキーボードが熱くなり指が火傷する:thinking:
   
などの弊害があり気軽にコードを変更して挙動を確かめてみるという確認が難しいです．

そこで外部にビルド環境だけ作って作業をするPCはコードを書くことだけに集中するという解決方法があります．イメージとしては以下の図のような感じです．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176701/de68adc0-b2de-74af-904a-bf9c43113df8.png)


[mainframer](https://github.com/buildfoundation/mainframer) のようなツールを使うことで比較的簡単に環境を構築することができます．用意しなければならないのは強いスペックのコンピュータだけなので，導入の敷居もそこまで高くないです．過去に私もこれに関して記事を書いています！

   - [mainframerで外部ビルド環境を作った話](https://chigichan24.hatenablog.com/entry/2018/04/18/203224)
   
また最近では[mirakle](https://github.com/Instamotor-Labs/mirakle)のような派生のものもできており，この辺のを使うともっと楽に環境を作ることができ，また外部にビルド環境があることを意識せずに開発できそうです:eyes:


## ディスプレイ (余談)
- 外部ディスプレイは解像度が高いほど・大きいほど良いという冗談はさておき，自分の**脳の記憶リソースをフルに活用できる程度**にディスプレイは拡張するべきだと思っています．
- 特に大規模なコードを読み解くときはクラスやメソッドの概観をさっと理解できる方がいいため，高解像度なディスプレイを縦置きにするのを私は好んでいます．

# 2. 読み解くためのフロー・tips集編
前半はフローについて詳細に説明し，後半は状況ごとに私が行っている手法をtips的に示したいと思います．

## 1. ドキュメントがないか探す
アーキテクチャや設計の方針に関して過去にまとめた資料，どういう基準でパッケージ・モジュール・クラス・メソッドが切り出されているのか等を知る．このときドキュメントが更新されているのかにも目を通す．

ドキュメントに方針がまとまっているならば何かを追加しよう・削除しよう・変更しようという操作に対しても即座に「**どこに**」，「**なにを**」書けばいいのかがわかります．

## 2. テストコードを見る
### UnitTest
テストコードはUnitTestであれば，メソッドやクラスの動作をどうしたいかを完結に記述してあります．もしそのメソッドを変更したいのであればまっさきにテストを参照するのが良いと考えています．

また，TDDの観点からもTestをまず変更した後に実装を変更するというのがバグの少なさと開発速度の双方を維持する方法だと考えています．

### Instrumentation test

AndroidではUIテストや統合テストのようなものは`AndroidTest`のディレクトリ以下に保持してあります．もしこれがあるなら操作全体の概要がつかみやすくなります．

特に統合テストではアプリの中でも重要なフローに関して担保しているケースも多く，このフローにどんなクラスがどのような流れで呼び出されているのかがわかる様になっています．


## 3. git blame(Pull Request)を見る
過去にどんな変更によってある特定の行が改善されたのかは**git blameを見てcommit hashを引っ張り出しそれからPRをたどる**ことである程度把握することができます．

AndroidStudioでは適当な行番号で右クリックし`Annotate`を表示します．すると各行で直近誰がどんなコミットが打ったか？またそのコミットで同時にどんなファイルが変更されたかが確認できます．これを用いてどういうファイルが関連ファイルなのかを知ることができます．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176701/2cbb4a24-b8b5-c31f-5cc4-b14b5c98249c.png)

また，あるコミットでの変更のみを見たいのであればAndroidStudio上のみでも完結します．上の図の1.のAnnotateを表示した後に任意のコミットを押すと下の図左に示すものが出てきます．ここで，赤で囲んだボタンを押すと右図のように差分がいい感じに出てきます．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176701/37cd9534-53b9-a0e2-4834-7c609161a1d5.png)

## 4. クラスの関係を読み解く
ここまで来たら，だいたいどこに何があるかがわかるはずです，細かな呼び出しは

- `[command]` + `b`(定義元へジャンプ)
- `[command]` + `]`, `[`，(戻る・進む)
- `[command]` + `[shift]` + `f` (全体検索)
- `[command]` + `[shift]` + `o` (ファイル検索)

を使いながら確認していきます．この時大規模なコードだと`[shift]` `[shift]`は異常に重くてもっさりする感じがあるので，なるべく使わずに行きましょう．

また，戻る・進むだけでは元いたファイルに飛ぶのがなんとなく難しいという問題があることもあります．そういうときは私は `[command]` + `[shift]` + `e`を使って直近開いたファイル一覧をみながら戻るようにしています．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176701/a25817b2-a72c-1b20-76d1-b954e1473eb0.png)


クラスの関係は読み解くのが難しいことも多いので以下ではtipsを紹介します．

### 4.1 [tips] Daggerのmoduleを見る
これはDaggerを使っている場合のみのテクニックですが，moduleで何をprovideMethodとしているかを見るという方法です．これを見ると依存関係の一部が見えてきます．このManagerクラス様々なところで`@Inject`されてるけど一体どうなってるんだこれってときとかに便利です．

### 4.2 [tips] string resouceから逆引きテク
編集したい画面(Activity / Fragment)が見つからないときに探す方法

1. アプリ内で使われている固有っぽい文字列を検索し，そのstring resourceを見つける．
2. 見つけた`@string/hoge_piyo`を更に検索し，これを呼び出しているlayout resourceを見つける．
3. 見つけたlayout resouceを使用している画面を見つけて完了

このテクは私自身は出社初日とかに全体概要を掴むためによく使っています．段々と勝手がわかってくるのでそう頻繁にはしないですが...

### 4.3 [tips] マルチモジュールの図を見る
マルチモジュール構成の場合の依存関係をさっと確認したいときはDroidKaigi 2019のアプリでも用いられていましたが，以下のgradle taskを定義してあげて実行しています．

[Gradle Script](https://github.com/JakeWharton/SdkSearch/blob/3351cad9bfacb0a364858e843774147143f58c7a/gradle/projectDependencyGraph.gradle)
[DroidKaigi2019での使用例](https://github.com/DroidKaigi/conference-app-2019/blob/master/gradle/dependencyGraph.gradle)

### 4.4 [tips] FindUsagesを見る
どこで使われているかを知ることでなんのために作られたのかがわかることがあります．

### 4.5 [tips] 検索をするときは絞り込む
例えば拡張子(`.java` / `.kt` / `.xml`)やパッケージ，モジュールを設定するだけでも検索は高速になります．何度も書いていますが`shift` `shift`は重いので，何を検索したいのかを明確にして使い分けましょう．

### 4.6 [tips] GitHubでググる
よくわからんテクっぽいことが発生しているときにはGitHubの検索をすると他の人も同様に似た操作をしているケースが割とあります．そこでは操作の詳細を明確に記述している場合もあるので，それを参考にしましょう．

### 4.7 [tips] アプリのエントリーポイントから特定のフローが来るまでは追わないほうがいい
なれないうちに全貌を解き明かすのは無理なので，MainActivityからFragmentの呼び出しを追いながら導線を確認するのは無駄に時間がかかる傾向にあります．

# 3. 読みやすいコードにするためにリファクタする編
ここまでを説明して分かる通り，大規模化したアプリの一部はレガシー化していて誰も手がつけられずその全貌を読み解くのに時間がかかってしまうことがあります．

## どんなコードが読みやすい・扱いやすいのか？
原則(一定のルール)に則っていることが最も大切だと考えています．それはSOLIDやYANGIのような小さな範囲のものからパッケージ6原則のような中程度のもの，アプリアーキテクチャ(MV-What, Flux, CleanArchitecture)のような広い範囲のものまで様々ですが，プロジェクトごとに**ドキュメント**の形で残るのが理想だと私は考えています．

また，それを担保するための仕組みとしての**テスト**が導入されると嬉しいなと思います．

ここで勘のいい読者の方は気がついたと思いますが，これは読み解くときのフローでたどっていた順番に等しいのです．読み手になると初めてどんなコードが求められるのかが分かった気がしますね．

つまり読みやすいコードにリファクタする手順は，**読み解くときにたどったフローでなかったものを追加する**ことで改善しリファクタできると言えます．

# まとめ

- 1. 大規模化したコードにストレスなく対応できる環境つくり編
- 2. 読み解くためのフロー・tips集編
- 3. 読みやすいコードにするためにリファクタする編

の3つに大きく分けて私自身が行っている読み方を書きました．最後のリファクタする編は少し薄い内容になってしまいました．．．ごめんなさい．これについてはまた[別の機会](https://droidkaigi.jp/2020/)で詳しく話していけたらなと思います．ここで書いたことが誰かの役に立てばいいなと思います :pray: