はじめに
前回 に引き続いて実行構成の話です。今回はデバッガについて説明します。とあるテスト駆動開発(TDD)の流派ではデバッガを使うことは死を意味すると言いますが、IDEを使っていてデバッガを使わないのは、IDEの機能の1/3くらいを捨てているようなものだと思っています。
デバッガに関してはちょうどEclipse向きのすばらしいスライドがありますので、これをベースにAndroid Studioではどうなのか?という視点で進めていきます。
Eclipseデバッガを活用するための31のTips
@kompiro さん、ありがとうございました。
デバッガの基礎
Android Studioのデバッガの基本的な使い方を紹介します。説明の都合上「Android Application」の実行構成を例に進めていきます。
デバッガを使うには
まずはブレイクポイントを設定します。Javaのソースコードをエディタで開き、ブレイクポイントを設定したい行の左端(ガーターエリア )をクリックします。
図1 ブレイクポイントの設置
※ もう一度クリックするとブレイクポイントを解除します。
たいていのAndroidアプリケーションは実行すると所定のアクティビティで待機状態になるため、デバッガ実行後にブレイクポイントを設定しても間に合います。Androidテストのように実行したら待機せずに順次実行してしまう場合は、デバッガ実行まえにブレイクポイントを設定しておきましょう。
実行構成をデバッガモードで実行してアプリケーションのデバッグを行います。カレントの実行構成をデバッガモードで実行するには、メニューバーの「Run」メニュー、またはツールバーにある「 Debug」を実行します。
通常実行時と異なり「Debugツールウィンドウ」が表示されます。このツールウィンドウがいわゆるデバッガになります。指定したブレイクポイントまでプログラムが実行されると「Debugツールウィンドウ」にその情報が表示されます。
Debugツールウィンドウについて
「Debugツールウィンドウ」は図2 のようなペイン(タブ)で構成されています。
図2 「Debugツールウィンドウ」
前回 紹介した「Androidツールウィンドウ」のようにレイアウトを自由に変更することができます。ツールバーある(「Androidツールウィンドウ」には無かった)「 Restore Layout」アイコンを押すとレイアウトが復元されるので、臆せずいろいろ試してみてください。
実行中のオブジェクトの値をみる
ブレイクポイントで停止すると「Variablesペイン」にその場で参照可能なオブジェクトが一覧表示されます。
図3 「Variablesペイン」
「Variablesペイン」から特定のオブジェクトを選択し、コンテキストメニューから"Inspect "を実行すると、そのオブジェクトのみを切り出した「Inspectポップアップ」が表示されます。一時的に特定のオブジェクトを掘り下げて覗いてみたい時に便利です。
図4 「Inspectポップアップ」
また、エディタ上で「Variablesタブ」にリストアップされているオブジェクト(変数や引数)に対応する対象にマウスカーソルをあてると図5 のように、オブジェクトの値がポップアップされます。
図5 エディタ上で変数の内容を参照する
さらに、そのポップアップをクリックすると図6 のようなインスペクション画面に切り替わります。使いようによっては、先ほどの「Inspectポップアップ」より便利です。
図6 さらにポップアップをクリックするとインスペクション画面に切り替わる
「Variablesペイン」には主に変数や引数がリストアップされます。それらオブジェクトの特定のメンバや、オブジェクトを組み合わせた式の結果を参照したい場合は「ウォッチ式」を用います。「Variablesペイン」上の任意のオブジェクト、またはエディタ上で(式として評価可能な)任意の範囲を選択中にコンテキストメニューから"Add to Watches "を実行すると、オブジェクトや式が「Watchesペイン」に表示されます。
図7 ウォッチ式の追加
※「ウォッチ式」と呼ぶだけあって「Watchesタブ」には式が書けます。
「Watchesペイン」上の「+」アイコンからもウォッチ式を追加することができます。この場合「Watchesペイン」上に空欄ができあがりますので、そこに任意のウォッチ式を記述します。
図8 「Watchesペイン」から直接ウォッチ式を記述する
意外かも知れませんが、ウォッチ式はコード補完が可能です。「+」アイコンによる新規入力時だけではなく、既存のウォッチ式の編集時もコード補完ができます。
先ほど紹介したエディタ上でのオブジェクトの値参照の延長線上にある操作なのですが、ALT(opt)キーを押しながらエディタ上にマウスカーソルを置くと、マウスカーソルの位置に応じて参照可能な領域に下線が引かれます。
利用者が望む領域に下線が引かれたなら、それをクリックすると図9 のようにインスペクション画面が表示されます(プリミティブ型の場合、ツールチップとして表示されます)。
図9 ALT(opt)キーを押しながらマウスオーバーする(クリックすると動きがわかります)
マウスカーソルの移動による下線の設定がうまくいかずイライラする場合は、評価したい範囲を選択してからALT(opt)キーをクリックしても良いです。
図10 任意の領域を指定してALT+マウスクリックしても良い
個人的には自動的にポップアップする方法より、こちらの方が便利だと思っています。なお、この操作のモディファイアキーを他のキーに変更する事はできません。
実行中のオブジェクトの値を書き換える
デバッガらしく「Variablesタブ」や「Watchesタブ」でリストアップしているオブジェクトの値を変更してみましょう。変更したいオブジェクトを選択し、コンテキストメニューから"Set Value "を実行します。
図11 "Set Value"の実行例(クリックすると動きがわかります)
文字列やint, long
などのプリミティブ型の場合、代入する値は容易に想像がつくでしょう。当然、オブジェクトの値も変更可能で、以下のような代入方法があります。
new
演算子で新しいインスタンスを生成して代入します
他の変数名を代入すると、そのインスタンスの値になります
null
も代入できます
図12 "Set Value"でオブジェクトの値を変更する(クリックすると動きがわかります)
デバッグ中の操作(ステップ実行)
デバッガの操作は基本的にブレイクポイントで停止してからのステップ実行です。この操作はIDEに関わりなく共通しているのですが、どうゆうわけかそのショートカットキーや用語はIDEごとに微妙に異なります。この不統一感は、ほとんど嫌がらせのレベルだと思っています。
ステップ実行に関する基本的な操作は以下のとおりです。ショートカットキーは「Defaultキーマップ」のもので、参考までにEclipseとの対比も載せておきます。
Step Over
次の命令に移動します。ショートカットキーは F8 ( Eclipseは F6) 。
Step Into
カーソル位置のメソッドの中に移動します。ショートカットキーは F7 ( Eclipseは F5) 。
Step Out
今のメソッドから抜け、呼び出し元に移動します。ショートカットキーは SHIFT+F8 (Eclipseの Step Return(F7)に相当します) 。
Run To Cursor
カーソル位置まで処理を進めます。ショートカットキーは ALT+F9 (Eclipseの Run to Line(Ctrl+R)に相当します) 。
Resume Program
次のブレイクポイントまで処理を進めます。ショートカットキーは F9 (Eclipseの Resume(F8)に相当します)
Android Studioならではの機能に「 "Smart Step Into "」があります。通常の"Step Into "とは異なり、ステップインする候補を選び、こちらが望んだ場所でステップインできます。
図13 "Smart Step Into"の実行例(クリックすると動きがわかります)
ライブラリにステップインする場合(ソースコードのアタッチ)
なにもデバッガに限った話ではありませんが、Anrdoi SDKや外部ライブラリにステップインするには、そのソースコードが参照可能な状態になってなければなりません。Android SDKの場合は、SDK Managerで対応するSDKのソースコードを入手可能です。
図14 Android SDK ManagerでSDKのソースコードを入手する
Android Studioで使っているAndroid SDKにソースコードが設定されているかどうかは、メニューバーの「File → Other Settings → Default Project Structure...」の「Project Structure」設定画面の「SDKs / <Android SDKのバージョン>/ Sourcepath」で確認できます。
図15 デフォルトの「Project Structure」設定画面
ライブラリの場合どうなるでしょう。今のところ build.gradle
の dependencies
にソースコードやJavadocを取得する記述はありません。当然のように「Project Structure」設定画面にもライブラリにソースコードを割り当てる項目がないです。
図16 Android Studioではライブラリの設定画面が隠されている
では、どうするか?と言えば、何も気にせずデバッガを実行して外部ライブラリにステップインします。ソースコードが無いライブラリにステップインすると、そのエディタタブの右上端に図17 のようなメッセージが表示されます。
図17 エディタでソースコードの無いライブラリを開いた場合
参照しているライブラリがオープンソース系でインターネット(Mavenリポジトリ)にソースコードが公開されているのであれば「Search in internet...」をクリックします。すると該当するソースコードを探し当て、そのライブラリに割り当てます。
図18 ソースコードが設定されたライブラリの例
ちなみに、この方法で入手したライブラリのソースコードは <HOME>/.ideaLibSources
に保存されます(<PROJECT_HOME>/.idea/libraries/<ライブラリ名>.xml
で確認できます) 。
不幸にもインターネットに接続してない環境にいる、もしくはライブラリそのものがインターネット上にソースコードが公開されていない場合は「Attach Sources...」をクリックして、ローカルにあるソースコードを指定します。
ブレイクポイントの操作
エディタの左端(ガーターエリア)をクリックすることでブレイクポイントのON/OFFができますが、ブレイクポイントを消すのではなく、ブレイクポイントを無効化 したい場合は、ALT(opt)キーを押しながらブレイクポイントをクリックします。
図19 無効化されたブレイクポイント
エディタのガーターエリアをクリックして設定できるブレイクポイントは「行ブレイクポイント」だけなのですが、Android Studioが設定できるブレイクポイントは他にも種類があります。Android Studioが設定できるブレイクポイントは次のとおりです。
行ブレイクポイント(Line Breakpoints)
メソッドブレイクポイント(Method Breakpoints)
フィールドウォッチポイント(Field Watchpoints)
例外ブレイクポイント(Exception Breakpoints)
メソッドブレイクポイントやフィールドブレイクポイントの設置は「Run」メニューから行います。
図20 「 Run」メニューにあるブレイクポイント系のコマンド
例外ブレイクポイントに関してはブレイクポイントの設定画面("View Breakpoints... "を実行すると表示される)からでしか設定できません。
どれもブレイクポイントには変わりありませんが、プログラムを止める条件や振る舞いが異なるためそれぞれ異なるブレイクポイントとして提供しています。ブレイクポイントの条件にその違いが見て取れます。
ブレイクポイントの設定変更は"View Breakpoints... "から呼び出す「Breakpoints」ダイアログから行いますが、ガーターエリア上のブレイクポイントを右クリックすると簡易な設定ができるポップアップが表示されます。
この設定ポップアップを元に、それぞれのブレイクポイントの特徴を紹介します。
行ブレイクポイント
もっとも頻繁に使うブレイクポイントで、プログラムの実行が指定した行に来たらプログラムを一時停止します。
メソッドの宣言文に設定すれば、そのメソッドを呼び出した時に反応してプログラムを一時停止します(呼出しのみですが限定的なメソッドブレイクポイントとしても機能するわけです) 。またクラスの宣言文に設定すると、そのクラスがロードするタイミングでプログラムを一時停止します。
設定項目もいたってシンプルです。
図21 行ブレイクポイントの設定ポップアップの例
「Run」メニューにある"Toggle Temporary Line Breakpoint "は「一度とまったら消えて無くブレイクポイント 」のことです。実体は通常の行ブレイクポイントにオプション「Remove once hit」が設定されているだけです。
図22 「 Temporary Line Breakpoint」の例
メソッドブレイクポイント
メソッドに特化したブレイクポイントです。メソッドの宣言文にしか設置できません。プログラムを停止する条件として、次の2つを設定できます。
メソッドを呼び出した時(Method entry)
メソッドが終了した時(Method exit)
図23 メソッドブレイクポイントの設定ポップアップの例
フィールドウォッチポイント
フィールドに特化したブレイクポイントです。フィールド宣言文に行ブレイクポイントを設定すると、そのフィールドの初期化時にプログラムを一時停止します。
フィールドウォッチポイントの場合、そのフィールドの値が参照された(Field access) /更新された(Field modification)かによってプログラムを一時停止します。
図24 フィールドウォッチポイントの設定ポップアップの例
残念なことに、Androidアプリケーションのデバッグでは、このフィールドウォッチポイントは機能しませんでした。
例外ブレイクポイント
例外に特化したブレイクポイントです。指定した例外が送出されるとプログラムを一時停止します。
例外ブレイクポイントはエディタ上には現れません。「 Breakpoints」ダイアログから「Exception Breakpoints」を追加して設定します。
図25 「 Exception Breakpoints」を追加する
※この後、監視対象とする例外クラスを選択するダイアログが表示されます。
あらかじめ設定されている「Any exception」を有効にしてもよいです。この場合、すべての例外が監視対象になります。
図26 「 Any exception」を有効にする
すべてのブレイクポイントを一時的に無効にしたい場合は「Debugツールウィンドウ」の"Mute Breakpoints "を実行します。
図27 ブレイクポイントのミュート(無効化)
このコマンド(アイコン)はトグルになっているので、ミュートしたなら、もう一度"Mute Breakpoints "を実行するまで解除されません。
"Mute Breakpoints "とは若干機能が異なるのですが「Breakpoints」ダイアログから任意のブレイクポイントを無効化することもできます。ダイアログ左上のツールバーでブレイクポイントをグループ化しておくと、ある程度範囲を絞り込んだ無効化ができます。
図28 「 Breakpoints」ダイアログからブレイクポイントを無効化する(クリックすると動きがわかります)
素早くトレースログを設定する
いくら時代が進もうと古典的なトレースログ(デバッグプリント)はデバッグの強い味方です。エディタ上で行ブレイクポイントを設定するときに、SHIFTキーと一緒にマウスをクリックすると図29 のようなログ出力付きのブレイクポイントが設置されます。
図29 トレースログ用のブレイクポイント
この方法(SHIFT+クリック)で設置したブレイクポイントは、設置した行の情報をログに書き出すだけでなく、ブレイクポイントに到達してもプログラムを停止させません。そのため、プログラムはするする動き、どこを通ったかがログに出力されるだけ、という変わり種のブレイクポイントです。
図30 「 Debugペイン」に出力されたトレースログ
比較的しょうもない系の機能ですが、気楽に設置(SHIFT+クリック)できることと、ソースコード自体をデバッグプリントで汚さないので、個人的に気に入っている機能のひとつです。
応用編
応用編ではデバッガに関するTIPSや操作をQ&A方式で紹介します。
Hotswapはできないの?
デバッグ中にクラスファイルを入れ替えるHotswapですが、通常のJavaアプリケーションなら可能ですが、Androidアプリケーション相手にはできません。
既存のAndroidアプリケーションにデバッガ接続するには?
Androidエミュレータ、または実機にてデバッグしたいアプリケーションを実行しておき、ツールバーにある"Attach debugger to Android process "を使って接続します。
図31 "Attach debugger to Android process"の実行例
「Debugツールウィンドウ」にある「 "Rerun "」より、素早くデバッグを開始できるのでオススメです。
なお、プログラムを修正して、それを再度デプロイしてデバッグを継続したい場合は、"Attach debugger to Android process "でも「Debugツールウィンドウ」の"Rerun "ではなく、"Debug "を実行します。Android Studioでは、こうしないと再デプロイが行われません。
デバッグ中のフレームの操作はできますか?
できるけど、できません。Android Studioの機能としてフレーム操作を持っているのですが、Androidアプリケーションでは一部の機能が反応しません。
図32 Androidアプリケーションでは"Drop Frame"が使えない
フレームの破棄("Drop Frame ")はできませんが、「 Framesペイン」から特定のフレームを参照することは可能です。ただし、Eclipseのようにコールスタックをコピペする機能はありません。
より高度なブレイクポイントの設定はできますか?
できます。ブレイクポイントを右クリックして詳細設定をポップアップさせるか、"View Breakpoints... "で表示する「Breakpoints」ダイアログから設定できます。
スレッドの停止・全体の停止
「Suspend」で、ブレイクポイントに到達したときに、全体を停止するか(All) /該当するスレッドのみを停止するか(Thread)を指定します。また「Suspend」そのものもON/OFF可能で、OFFにすれば停止せずに素通りします。
特定の条件を満たしたときにブレイクポイントを有効にする
「Condition」に式を書き、その式が true
のときにブレイクポイントが有効になります。「 Condition」には、ブレイクポイント近辺のコンテキスト(変数や引数など)が利用できます。どのようなコンテキストが利用できるかは「Condition」でのコード補完で確認することができます。
図33 「 Condition」でもコード補完が利く
インスタンスを指定したブレイクポイント
「Instance fileters」にインスタンスIDを指定することで、特定のインスタンスでのみ反応するブレイクポイントを作ることができます。指定できるインスタンスIDはlong
の整数(16進表記はNG)で、スペース区切りで複数指定できます。
呼び出し回数の設定
「Pass count」に数字を設定すると、そのブレイクポイントは指定した値に達するまでプログラムを停止しません。
他のブレイクポイントと連動するブレイクポイント
「指定したブレイクポイントを通過した時に有効になるブレイクポイント」です。ユニークな機能なのですが、イマイチ使いどころを見つけられずにいます。
図34 2つのブレイクポイント
図35 のようなケースで「( 2)は(1)を通過したときのみ有効」になって欲しいとき、次のように設定を行います。
図35 ( 2)のブレイクポイントの設定
(2)のブレイクポイント側で「Disabled until selected breakpoint is hit」に(1)のブレイクポイントを指定します。これにより(2)は(1)を通過するまで有効になりません。「 After breakpoint was hit」は(2)のブレイクポイントに到達した後の処理を指定します。再び無効にするなら「Disable again」を、有効にしたままにするなら「Leave enabled」を指定します。
Eclipseの場合、この他に「特定の変数の値が変わったどうか」を条件に設定できるようですが、Android Studioにはその機能はありません。
「Class filters」って何に使うの?
ブレイクポイントの設定画面にある「Class filters」ですが、本編ではしれっと無視していましたが、これはいったい何でしょう?「Instance filters」がインスタンスIDでブレイクポイントをフィルタするように、こちらはクラスを指定してブレイクポイントをフィルタします。しかし行ブレイクポイントにしろ、メソッドブレイクポイントにしろ、特定のクラスに対してブレイクポイントを設定しているのに「Class filters」とは不思議な話です。
あまり知られていませんが、メソッドブレイクポイントは「特定のクラス」ではなく「クラスのパターン」に対して設定することができます。「 Breakpoints」ダイアログでメソッドブレイクポイントを追加するときのみ、図36 のように「Class pattern」を設定することができます。ただし、パターン指定できるのはクラスのみで、メソッドにパターンは指定できません。
図36 「 Add Method Breakpoint」ダイアログ(クリックすると動きがわかります)
※ バグか仕様かわかりませんが、Class patternには制約があるようです("*List
"はOKだが、"Note*
"のようなパターンはNG) 。
このようなケースだと「Class filters」も意味があります。「 Class filters」をONにして「...」ボタンを押すと図37 のような「Class Filters」ダイアログが表示されるので、有効にしたいクラス(Class Filters)や無視したいクラス(Class Exclusion Filters)をそれぞれ設定します。
図37 「 Class Filters」ダイアログ
個人的には、ここまでブレイクポイントを活用するのはやり過ぎだと思います。この記事を執筆するため実機検証したときに何度が遭遇したのですが、デバッガを起動してもAndroidアプリケーションが「Waiting For Debugger」のまま先に進まないことがありました。
たいていはメソッドブレイクポイントの設定間違いだったのですが、そこに辿り着くまですごくハマりました。同じ轍を踏まないよう気をつけてください。
図38 変なところにブレイクポイントを仕込んだ結果
インスペクタでオブジェクトの参照元は見れますか?
見れません。もともと、そうゆう機能がないので「Variablesペイン」や「Watchesペイン」でオプションやコマンドを探しても見つかりません。
変数の値をわかりやすく表示することはできますか?
できます。簡単な方法とものすごく面倒な方法とで何通りか方法があります。以下の操作は、特に断りがなければ「Variablesペイン」や「Watchesペイン」での操作になります。
プリミティブ型の場合
コンテキストメニューから"View as "を選び、16進表記にするか10進表記にするかを指定できます。
図39 "View as"でプリミティブ型の表示方法を変更する
配列やリストの場合
コンテキストメニューから"Adjest Range... "を選び、配列やリストの表示範囲を設定することができます。
図40 Adjest Rangeで配列やリストの表示範囲を設定(クリックすると動きがわかります)
オブジェクトの表示方法を個別に指定する
コンテキストメニューから「Customize Data Views...」を選び、表示されるダイアログの「Data Type Renderersタブ」でオブジェクトごとの表示方法を指定します。
図41 「 Customize Data Views / Data Type Renderers」設定画面
まずは設定の名前(Renderer name)を決め、対象となるクラス(Apply renderer to objects of type)を指定します。その後、ノードを折り畳んでいる時の表示方法(When rendering the node)と、ノードを展開した時の表示方法(When expanding the node)をそれぞれ指定します。
ここで設定した名前(Renderer name)は「Variablesペイン」や「Watchesペイン」の「View as」で指定できるようになります。
図42 "View as"でカスタマイズした表示方法を指定する
「Variablesペイン」「Watchesペイン」のカスタマイズ
コンテキストメニューから「Customize Data Views...」を選び、表示されるダイアログの「Data Viewsタブ」で全体的な設定ができます。
図43 「 Customize Data Views / Data Views」設定画面
たとえば「Show」では「Variablesペイン」で表示する対象をそれぞれ指定します。「 Enable 'toString()' object view」では、toString()
表示するオブジェクトを指定することができます。
特定のパッケージやクラスにステップインさせないことはできますか?
できます。「 Preferences / Debugger / Stepping」でステップフィルタを設定できます。「 Do not step into the classes」をONにし、その下のエリアにステップインしたくないクラスやパッケージを指定します。
図44 「 Preferences / Debugger / Stepping」設定画面
同じく、この設定画面で「コンストラクタをスキップ(Skip constructors) 」したり「簡単なアクセサの呼出をスキップ(Skip simple getters) 」することもできます。
デバッグ中にプログラムとは関係のないコード片を実行できますか?
できます。「 Debugツールウィンドウ」のツールバーか「Run」メニューから「 "Evaluate Expression... "を実行すると、図45 のようなダイアログが表示されます。
図45 "Evaluate Expression..."を実行して表示されるダイアログ
「Expression Evaluation」ダイアログは「Expression」欄にコード片(1行)を記述して「Evaluate」ボタンでその式を評価します。「 Code Fragment Mode」ボタンを押すこと「Code Fragment Evaluation」ダイアログに切り替わり、ここでは複数行のコード片を記述して評価することが可能です。
「Code Fragment Mode」では任意の例外を送出できそうなんですが、実際にやってみても例外送出することはできません。
図46 例外は送出することができない
他になにか面白いことをおしえてください
オブジェクトにマークを付けることができます
「Variablesペイン」などでコンテキストメニューから"Mark Object "を実行すると、任意のオブジェクトに対してマークを付けることができます。
図47 "Mark Object"の実行例
マークは変数ではなく、その変数が指し示しているインスタンスに付きます。また、マークは文字列だけではなく色も付けることができるため、マークを付けたインスタンスが入れ替わったかどうかを容易に把握することが可能です。
またマークしたオブジェクトは <マーク名>_DebugLabel
としてブレイクポイントの条件式や "Evaluation Expression... " の評価式で利用することもできます。
図48 Evaluation Expressionの評価式でマークオブジェクトを指定
スレッドダンプを取得できます
「Debugツールウィンドウ」のツールバーか「Run」メニューから「 "Get thread dump "」を実行すると、その時点でのスレッドダンプを取得します。この時、ブレイクポイントでプログラムが停止しているかどうかは関係ありません。
図49 "Get thread dump"でスレッドダンプを取得する(クリックすると動きがわかります)
スレッドダンプを取得すると、「 Debugツールウィンドウ」にスレッドダンプごとの「Dumpタブ」が追加されます。そういえば侍 なんてツールもありましたね……。
コンソールのフォントや色を変更できます
デバッガに限った話ではありませんが「Debugツールウィンドウ」「 Androidツールウィンドウ」や「Terminalツールウィンドウ」のフォントサイズやカラーを変更したいな、と思ったことはありませんか?
これら全て同じ設定を共有していて「Preferences / Editor / Color&Fonts」の次の項目でそれぞれ設定変更できます。
Console Colors : 各種カラー設定を行います。
Console Font : フォントやフォントサイズを設定します。
図50 Preferences/Editor/Color&Fontsの設定
対象となるツールウィンドウによっては、一度ツールウィンドウを破棄してからではないと設定が反映されません(「 Terminalツールウィンドウ」がそうです) 。