はじめに
みなさん、Androidアプリのデバッグ時にアプリケーションコードにむやみにLog.d()
を埋めて、再ビルドを何回も繰り返しながらデバッグしていませんか?
もしくはブレークポイントでsuspendさせまくって、Resume Program
(suspend状態から再開するやつ)を連打していませんか?
でもアプリの規模が大きくなっていくと、再ビルドに掛かる時間も無視できなくなったり、Resume Program
の連打し過ぎで指が腱鞘炎になったりします。
そんなときはAndroid StudioのBreakpointsを上手く設定して、何度も「再ビルド→再インストール」やResume Program
連打から脱却しましょう。
目標
- デバッグのために何度も「
Log.d()
を埋め込んでアプリを再ビルド→再インストール」をやめる -
Resume Program
連打をやめる
環境
Android Studio3.5
Android 9(なんとなく)
「ブレークポイント」でログを出力する
実行時に通ったパスを知りたいけど、Log.d()
を埋めて再ビルドを待つのは嫌。でも、ブレークポイントを張りまくって毎回処理がsuspendされるのは鬱陶しい…
そんな時はブレークポイントを上手に設定して、suspendさせること無くログを出力しましょう。
手順
- いつも通り、対象の行にブレークポイントを張る
- Breakpoints(デフォルトではShift + Command + F8) > 左の一覧から該当のブレークポイントを選択 >
Suspend
のチェックを外す -
"Breakpoint hit" message
にチェックを付ける (お好みでStack traceも出力できます) -
DONE
をクリック(チェックボックスをクリックしただけだと反映されない)
ブレークポイントを設定したら、いつも通りデバッガをアタッチして実際に該当箇所の処理を実行するだけ。
該当する行が実行されると以下のようにログが出力されます。
※LogcatではなくDebuggerのConsoleタブに出力されるので注意が必要です
アプリを再ビルドすることなくBreakpoint reached at net.fdash.adventcalendartest.MainActivity.onCreate(MainActivity.kt:22)
というログを出力させることが出来ました!
ついでに変数の中身、関数の戻り値を出力したい
例えば、RecyclerViewのポジションやタップされた座標のようにどんどん変わっていく値を見るために、毎回ブレークポイントでsuspendさせて値を確認するのは効率が悪いですよね。
そんなときに↓のようなログを埋めてアプリを再ビルドしたことはありませんか?
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Log.d(TAG, "${position}番目のセルにViewHolderがbindされたよ")
実はこの場合も、アプリを再ビルドすることなく実現できます。
手順
- 先程と同様にsuspendさせないブレークポイントを設定する
-
Evaluate and log:
にチェックを入れテキストボックスに表示させたい文字列を入れる(コードの評価をしてくれるので、変数や関数呼び出しを含めることも可能)
あとは、同じようにデバッガをアタッチして該当するコードを実行するだけです。
アプリを再ビルドすることも、ViewHolderがbindされる度にsuspendされることもなく、何番目のセルにViewHolderがbindされたか知ることが出来ます。
ブレークポイントの条件いろいろ
ちなみに、ブレークポイントには細かく条件を設定することができます。
項目 | 説明 |
---|---|
Remove once hit |
初回到達時にブレークポイントが外れる |
Instance filters: |
指定したインスタンスID (MainActivity@10792 の10792 の部分)のインスタンスでだけブレークポイントが有効になる |
Class filters: |
指定したクラスの場合だけブレークポイントが有効になる 継承関係がある場合に、特定のサブクラスだけブレークポイントを有効(もしくは無効)などが可能 |
Pass count: |
該当する行に指定した回数到達したときにブレークポイントが有効になる ( 3 を指定すると3, 6, 9, ... 回目に到達した場合のみブレークポイントが有効) |
Caller filters: |
特定のクラスやメソッドから呼ばれた場合のみブレークポイントが有効(もしくは無効)になる 指定の仕方に癖があるので注意(例えば *foo* と指定すれば、foo を含むクラスやメソッドからの呼び出し時のみ有効/無効になる詳しくはjetbrainsのヘルプページを参照 |
Disable until breakpoint is hit: : |
ブレークポイントに依存関係を設定出来る。依存先のブレークポイントに到達するまでは自身のブレークポイントは無効だが、依存先のブレークポイントに到達したあとは自身のブレークポイントが有効になる。 |
何回処理が実行されたか知りたい
当然、ブレークポイントでsuspendもログ出力もさせないことが出来ます。
ただ、suspendもログ出力もしないブレークポイントに何の意味があるのでしょうか。
実は、DebuggerのConsoleタブ > Overheadの部分には、ブレークポイントを通過した回数とデバッグに掛かったオーバーヘッドが表示されています。(実際の処理に何ms掛かったかを表示するわけではないので注意)
つまり、ログ出力やsuspendしないブレークポイントも、「どこか分からないけど、何かの処理が沢山呼ばれてる...」時の調査に威力を発揮します。
アプリ起動時の処理をデバッグする
アプリ起動時にクラッシュする場合、原因を突き止めるためにブレークポイントを張って止めたいけど「起動後にデバッガをアタッチしたら間に合わないよ!」とか「毎回Debug
やりなおすの面倒!」ってなりますよね。
もしくは「バックグラウンドでプロセスがKILLされた後の復帰時にデバッガをアタッチしておきたい」という場合。
そんなときは、デバッガをアタッチするまでアプリが起動しないようにしてしまいましょう。
手順
デバッグ対象のアプリを起動(普通にランチャーから起動するだけ)
こうすることで、MainActivity#onCreate
も確実にブレークポイントで止める事ができます。
まとめ
- ブレークポイントはただ処理を中断するだけではない
- ブレークポイントを上手く使えば、デバッグのための再ビルド回数が削減出来る(開発が早くなる!)
- ブレークポイントを上手く使えば、
Resume Program
の連打が不要になる(腱鞘炎にならない!)
現場からは以上です。
明日は @ysat さんの【ActiveRecordを使いつつDBのデータをメモ化して扱ってみる】です。
お楽しみに!!