アプリをデバッグする

Android Studio はデバッガを備えており、以下の処理などを行うことができます。

  • デバイスを選択して、アプリをデバッグする。
  • Java、Kotlin、C / C++ のコード内にブレークポイントを設定する。
  • 実行時に変数や式を検証する。

このページでは、デバッガ操作の基本的な手順について説明します。詳細については、IntelliJ IDEA デバッグ ドキュメントをご覧ください。

デバッグを有効にする

デバッグを開始する前に、以下のように準備する必要があります。

  • LLDB をインストールします。

    プロジェクトに C / C++ コードが含まれている場合は、SDK Manager から LLDB をインストールする必要があります。

  • デバイス上でデバッグを有効にします。

    エミュレータを使用している場合は、デフォルトで有効になっています。コネクテッド デバイスの場合は、デバイス開発者向けオプションでデバッグを有効化する必要があります。

  • デバッグ可能なビルド バリアントを実行します。

    ビルド設定内に debuggable true を含むビルド バリアントを使用する必要があります。通常は、各 Android Studio プロジェクトに含まれているデフォルトの「debug」バリアントを選択するだけで済みます(ただし、build.gradle ファイル内には表示されません)。新しいデバッグ可能なビルドタイプを定義する場合は、ビルドタイプに「debuggable true」を追加する必要があります。

        android {
            buildTypes {
                customDebugType {
                    debuggable true
                    ...
                }
            }
        }
        

    このプロパティは、C / C++ コードを持つモジュールにも適用されます(jniDebuggable プロパティは使用されなくなりました)。

注: アプリが、同じくデバッグ対象のライブラリ モジュールに依存している場合、デバッグ シンボルを保持するため、そのライブラリも debuggable true を指定してパッケージ化する必要があります。アプリ プロジェクトのデバッグ可能バリアントが、ライブラリ モジュールのデバッグ可能バリアントを確実に受け取るようにするには、必ずライブラリの非デフォルト バージョンを公開するようにしてください。

デバッグを開始する

デバッグ セッションを開始する手順は次のとおりです。

  1. アプリコード内にブレークポイントを設定します。
  2. ツールバーの [Debug]()をクリックして、[Select Deployment Target] ウィンドウを表示します。

    [Debug] をクリックした後、[Select Deployment Target] ウィンドウ内にデバイスが表示されない場合は、USB 経由でデバイスを接続するか、[Create new virtual device] をクリックして、Android Emulator を使用する必要があります。

    [Select Deployment Target] ウィンドウではなく、「run」から「debug」に切り替えてよいか尋ねるダイアログが表示された場合、アプリがすでにデバイス上で稼働していることを示しており、デバッグを開始する際にアプリが再起動されます。アプリ インスタンスを稼働したままにしたい場合は、[Cancel Debug] をクリックして、代わりに、実行中のアプリにデバッガをアタッチします。

  3. デプロイ ターゲットを選択して、[OK] をクリックします。

    Android Studio が APK をビルドし、デバッグキーで APK に署名して、選択したデバイスに APK をインストールしてから APK を実行します。C / C++ コードをプロジェクトに追加した場合、Android Studio は、[Debug] ウィンドウ内で LLDB デバッガも実行し、ネイティブ コードをデバッグします。

  4. [Debug] ウィンドウが開かない場合は、[View] > [Tool Windows] > [Debug] を選択するか、ツール ウィンドウ バーで [Debug]()をクリックして、[Debugger] タブをクリックします(図 1 を参照)。

    図 1: 現在のスレッドと変数のオブジェクト ツリーが表示されている [Debugger] ウィンドウ

実行中のアプリにデバッガをアタッチする

アプリがすでにデバイス上で稼働している場合は、以下の手順により、アプリを再起動せずにデバッグを開始することができます。

  1. [Attach debugger to Android process]()をクリックします。
  2. [Choose Process] ダイアログで、デバッガをアタッチするプロセスを選択します。

    エミュレータやルート権限取得済みデバイスを使用している場合は、[Show all processes] をオンにすると、すべてのプロセスが表示されます。

    [Debugger] プルダウン メニューから、別のデバッグタイプを選択できます。デフォルトでは、Android Studio は Auto デバッグタイプを使用して、プロジェクトに含まれているコードの種類(Java または C / C++)に応じて最適なデバッガ オプションを選択します。

  3. [OK] をクリックします。

    [Debug] ウィンドウが表示されます。

注: Android Studio のデバッガとガベージ コレクタは緩く統合されています。Android 仮想マシンでは、デバッガが切断されるまで、デバッガが認識しているオブジェクトに対してガベージ コレクションは行われません。そのため、デバッガが接続されていると、オブジェクトのビルドに時間がかかる場合があります。たとえば、実行中のスレッドをデバッガが認識している場合、スレッドが終了した後も、デバッガが接続を解除するまでは、関連付けられている Thread オブジェクトに対してガベージ コレクションが行われることはありません。

デバッガタイプを変更する

Java / Kotlin コードをデバッグする場合と C / C++ コードをデバッグする場合では、異なるデバッガツールが必要となるため、Android Studio デバッガでは、使用するデバッガタイプを選択できるようになっています。デフォルトでは、Android Studio はプロジェクト内で検出された言語に基づいて、使用するデバッガを決定します(Auto デバッガタイプを使用します)。別のタイプを使用したい場合は、[Run] > [Edit Configurations] をクリックして表示されるデバッグ設定や、[Run] > [Attach debugger to Android process] をクリックして表示されるダイアログで、手動でデバッガを選択することができます。

利用できる主なデバッグタイプは次のとおりです。

  • Auto: デバッグするコードの最適なオプションを、Android Studio が自動的に選択するように指定する場合は、このタイプを選択します。たとえば、プロジェクトに C / C++ コードが含まれている場合、Android Studio は自動的に Dual デバッグタイプを使用します。そうでない場合、Android Studio は Java デバッグタイプを使用します。
  • Java: Java または Kotlin で記述されているコードだけをデバッグする場合は、このタイプを選択します。Java デバッガは、ネイティブ コード内に設定されているブレークポイントやウォッチを無視します。
  • Native: (C / C++ コードが含まれている場合のみ)LLDB だけを使用してコードをデバッグする場合は、このタイプを選択します。このデバッグタイプを使用した場合、Java デバッガのセッション ビューは利用できません。デフォルトでは、LLDB はネイティブ コードだけを検証し、Java コード内のブレークポイントを無視します。Java コードもデバッグしたい場合は、Auto デバッグタイプか Dual デバッグタイプに切り替える必要があります。

    注: 32 ビット Windows 上で Android Studio 3.0 以降を使用している場合、ネイティブ デバッグは機能しません。32 ビット Windows を使用していて、ネイティブ コードをデバッグする必要がある場合は、Android Studio 2.3 を使用してください。

  • Dual: (C / C++ コードが含まれている場合のみ)Java コードとネイティブ コードのデバッグを切り替える場合は、このタイプを選択します。Android Studio は、Java デバッガと LLDB をそれぞれ別のアプリプロセスにアタッチするため、アプリの再起動やデバッグ設定の変更を行うことなく、Java コードとネイティブ コードの両方を対象にブレークポイントを検証することができます。

    図 2 のように、[Debug] ウィンドウのタイトルの右側に、2 つのタブが表示されます。タブが 2 つあるのは、アプリに Java コードと C++ コードの両方が含まれているためであり、1 つのタブはネイティブ コードのデバッグ用で、もう 1 つのタブ(「-java」付き)は Java コードのデバッグ用になります。

    図 2: ネイティブ コードをデバッグするためのタブと Java コードをデバッグするためのタブ

注: コンパイラによって最適化されたネイティブ コードをデバッグすると、「This function was compiled with optimizations enabled. Some debugger features may not be available」という警告メッセージが表示される場合があります。-O フラグなどの最適化フラグを使用した場合、コンパイラは、コンパイルするコードを効率的に実行できるように変更を加えます。最適化されたコンパイル済みコードは元のソースコードにマッピングするのが困難なため、デバッガから想定外の情報や誤った情報が返される可能性があります。そのため、ネイティブ コードをデバッグする際は、コンパイラの最適化を無効にする必要があります。

システムログを使用する

アプリをデバッグしているとき、システムログにシステム メッセージが表示されます。このメッセージには、デバイス上で実行中のアプリの情報が含まれています。システムログを使用してアプリをデバッグするには、アプリが開発段階にあるときに、コードを使用してログメッセージを書き込み、例外のスタック トレースを出力するように設定する必要があります。

コードを使用してログメッセージを書き込む

コードを使用してログメッセージを書き込むには、 Log クラスを使用します。ログメッセージを活用すると、アプリを操作しているときのシステム デバッグ出力を収集して、実行フローを把握することができます。また、ログメッセージから、アプリ内のエラーを発見することもできます。ログの詳細については、ログを書き込み、表示するをご覧ください。

アクティビティを開始する際に以前の状態情報が利用できるかどうかを判定するために、ログメッセージを追加する方法を、次のサンプルコードに示します。

Kotlin

    import android.util.Log
    ...
    private val TAG: String = MyActivity::class.java.simpleName
    ...
    class MyActivity : Activity() {
        ...
        override fun onCreate(savedInstanceState: Bundle?) {
            ...
            if (savedInstanceState != null) {
                Log.d(TAG, "onCreate() Restoring previous state")
                /* restore state */
            } else {
                Log.d(TAG, "onCreate() No saved state available")
                /* initialize app */
            }
        }
    }
    

Java

    import android.util.Log;
    ...
    public class MyActivity extends Activity {
        private static final String TAG = MyActivity.class.getSimpleName();
        ...
        @Override
        public void onCreate(Bundle savedInstanceState) {
           ...
           if (savedInstanceState != null) {
                Log.d(TAG, "onCreate() Restoring previous state");
                /* restore state */
            } else {
                Log.d(TAG, "onCreate() No saved state available");
                /* initialize app */
            }
        }
    }
    

開発中に、コードを使用して例外を検出し、スタック トレースをシステムログに書き込むことができます。

Kotlin

    fun someOtherMethod() {
        try {
            ...
        } catch (e : SomeException) {
            Log.d(TAG, "someOtherMethod()", e)
        }
    }
    

Java

    void someOtherMethod() {
        try {
            ...
        } catch (SomeException e) {
            Log.d(TAG, "someOtherMethod()", e);
        }
    }
    

注: アプリを公開する準備が整ったら、デバッグ ログメッセージとスタック トレース出力呼び出しをコードから削除します。そのためには、DEBUG フラグをセットして、条件ステートメント内にデバッグ ログメッセージを配置します。

システムログを表示する

[Logcat] ウィンドウで、デバッグ メッセージや各種システム メッセージを表示し、フィルタリングすることができます。たとえば、ガベージ コレクションが発生したときのメッセージや、Log クラスを使用してアプリに追加したメッセージを表示できます。

logcat を使用するには、デバッグを開始して、下部にあるツールバーで [Logcat] タブを選択します(図 3 を参照)。

図 3: [Logcat] ウィンドウとフィルタ設定

logcat とフィルタリング オプションの詳細については、logcat を使用してログを書き込み、表示するをご覧ください。

ブレークポイントを利用する

Android Studio は、さまざまなデバッグ アクションをトリガーするいくつかのタイプのブレークポイントをサポートしています。最も一般的なタイプは、コード内の指定行でアプリの実行を一時停止する行ブレークポイントです。実行が一時停止しているときに、変数や式を検証し、1 行ずつ実行を続けることで、ランタイム エラーの原因を特定することができます。

行ブレークポイントを追加するには:

  1. 実行を一時停止するコード行を特定して、そのコード行の左ガターをクリックするか、行にキャレットを配置して、Ctrl+F8(Mac の場合は Command+F8)を押します。
  2. アプリがすでに稼働している場合、アプリをアップデートしてブレークポイントを追加する必要はなく、[Attach debugger to Android process]()をクリックするだけで済みます。そうでない場合は、[Debug]()をクリックして、デバッグを開始します。

図 3: ブレークポイントを設定した行の横に表示される赤色の点

コード実行がブレークポイントの位置に到達すると、Android Studio はアプリの実行を一時停止します。一時停止したら、[Debugger] タブのツールを使用して、アプリの状態を判断することができます。

  • 変数のオブジェクト ツリーを検証するには、[Variables] ビューで、そのツリーを展開します。[Variables] ビューが表示されていない場合は、[Restore Variables View]()をクリックします。

  • 現在の実行ポイントで式を評価するには、[Evaluate Expression]()をクリックします。

  • メソッドを入力せずに次のコード行に進むには、[Step Over]()をクリックします。

  • メソッド呼び出しの内部にある最初の行に進むには、[Step Into]()をクリックします。

  • 現在のメソッドの外部にある次の行に進むには、[Step Out]()をクリックします。

  • アプリの通常の実行を続けるには、[Resume Program]()をクリックします。

プロジェクトがネイティブ コードを使用している場合は、デフォルトで、Auto デバッグタイプが選択され、Java デバッガと LLDB がそれぞれ別のプロセスとしてアプリにアタッチされます。そのため、アプリの再起動や設定の変更を行うことなく、Java と C / C++ のブレークポイントの検証を切り替えることができます。

注: Android Studio が C / C++ コード内のブレークポイントを検出するには、Auto、Native、Dual など、LLDB をサポートするデバッグタイプを使用する必要があります。デバッグ設定を編集することにより、Android Studio が使用するデバッグタイプを変更することができます。各デバッグタイプの詳細については、デバッグタイプをご覧ください。

Android Studio がターゲット デバイスにアプリをデプロイすると、デバッガ プロセスごとのタブやデバッグ セッション ビューを備えた [Debug] ウィンドウが開きます(図 4 を参照)。

図 4: LLDB を使用してネイティブ コードをデバッグする

  1. LLDB デバッガが C / C++ コード内のブレークポイントに到達すると、Android Studio によって自動的に [<モジュール名>] タブに切り替わります。[Frames] パネル、[Variables] パネル、[Watches] パネルも表示されます。各パネルは、Java コードをデバッグする場合と同じように機能します。LLDB セッション ビューでは [Threads] パネルは利用できませんが、[Frames] パネルのプルダウン リストを使用することで、アプリプロセスにアクセスできます。各パネルの詳細については、ウィンドウ フレームをデバッグする変数を検証するをご覧ください。

    注: ネイティブ コード内のブレークポイントを検証しているときに、Android システムは、アプリの Java バイトコードを実行している仮想マシンを一時停止します。そのため、ネイティブ コード内のブレークポイントを検証している間は、Java デバッガを操作したり、Java デバッガ セッションから状態情報を取得したりすることはできません。

  2. Java デバッガが Java コード内のブレークポイントに到達すると、Android Studio によって自動的に [<モジュール名>-java] タブに切り替わります。
  3. LLDB を使用してデバッグしているときに、LLDB セッション ビュー内で LLDB ターミナルを使用すると、コマンドライン オプションを LLDB に渡すことができます。アプリのデバッグを開始するたびに、デバッガがアプリプロセスにアタッチする直前または直後に特定のコマンドを LLDB に実行させる場合は、そのコマンドをデバッグ設定に追加しておくと便利です。

C / C++ コードをデバッグしているときに、「ウォッチポイント」と呼ばれる特別なタイプのブレークポイントを設定できます。ウォッチポイントは、アプリが特定のメモリブロックを使用しているときに、アプリプロセスを一時停止することができます。詳細については、ウォッチポイントを追加するをご覧ください。

ブレークポイントを表示、設定する

すべてのブレークポイントを表示し、ブレークポイント設定を指定するには、[Debug] ウィンドウの左側にある [View Breakpoints]()をクリックします。[Breakpoints] ウィンドウが表示されます(図 5 を参照)。

図 5: 現在のすべてのブレークポイントをリスト表示し、各ブレークポイントの動作設定を表示する [Breakpoints] ウィンドウ

[Breakpoints] ウィンドウの左側のリストで、各ブレークポイントの有効 / 無効を切り替えることができます。ブレークポイントを無効にすると、そのブレークポイントにヒットしても、Android Studio はアプリを一時停止しません。リストからブレークポイントを選択すると、その設定を指定できます。一度ブレークポイントを無効にしておいて、別のブレークポイントにヒットした後に最初のブレークポイントを有効化するように設定することもできます。また、ブレークポイントにヒットした後で、そのブレークポイントを無効にするかどうかを設定することもできます。例外用のブレークポイントを設定するには、ブレークポイント リストで [Exception Breakpoints] を選択します。

ウィンドウ フレームをデバッグする

[Debugger] ウィンドウの [Frames] パネルで、現在のブレークポイントにヒットした原因となったスタック フレームを検査することができます。このパネルでは、スタック フレームに移動して調査したり、Android アプリ内のスレッドリストを検証したりすることができます。スレッドを選択するには、スレッド セレクタのプルダウン メニューを使用して、そのスタック フレームを表示します。フレーム内の要素をクリックすると、エディタ内にソースが開きます。また、スレッド表示のカスタマイズや、スタック フレームのエクスポートを行うことができます。詳細については、ウィンドウ フレーム ガイドをご覧ください。

変数を検証する

[Debugger] ウィンドウの [Variables] パネルでは、アプリがブレークポイントで停止したときに変数を検証できます。[Frames] パネルでは、フレームを選択できます。[Variables] パネルでは、静的メソッドを使用してアドホック式を検証したり、選択したフレーム内で利用できる変数を調査したりすることができます。

[Watches] パネルも同様の機能を備えています。ただし、[Watches] パネルに追加した式は、デバッグ セッション間も保持されます。頻繁にアクセスする変数やフィールド、あるいは現在のデバッグ セッションに役立つ状態情報を提供する変数やフィールドに対して、ウォッチを追加してください。[Variables] パネルや [Watches] パネルの外観については、図 6 をご覧ください。

[Watches] リストに変数や式を追加するには:

  1. デバッグを開始します。
  2. [Watches] パネルで、[Add]()をクリックします。
  3. 表示されたテキスト ボックスに、モニタリングする変数や式の名前を入力して、Enter キーを押します。

[Watches] リストから項目を削除するには、項目を選択して、[Remove]()をクリックします。

[Watches] リスト内の項目を選択して、[Up]()または [Down]()をクリックすると、リスト内の要素を並べ替えることができます。

図 6: [Debugger] ウィンドウの [Variables] パネルと [Watches] パネル

ウォッチポイントを追加する

C / C++ コードをデバッグしているときに、「ウォッチポイント」と呼ばれる特別なタイプのブレークポイントを設定できます。ウォッチポイントは、アプリが特定のメモリブロックを使用しているときに、アプリプロセスを一時停止することができます。たとえば、あるメモリブロックに 2 つのポインタを設定し、そのメモリブロックにウォッチポイントを割り当てた場合、いずれかのポインタを使用してそのメモリブロックにアクセスすると、ウォッチポイントがトリガーされます。

Android Studio では、特定の変数を選択することで、実行時にウォッチポイントを作成することができます。ただし、LLDB は、変数自体に対してではなく、その変数に割り当てられたメモリブロックに対してウォッチポイントを割り当てます。これは、変数を [Watches] パネルに追加することとは異なります。変数をこのパネルに追加した場合、変数の値をモニタリングすることはできますが、システムがメモリ内の値を読み取ったときや変更したときにアプリプロセスを一時停止することはできません。

注: アプリプロセスが関数を終了し、システムがメモリからローカル変数への割り当てを解除した場合は、変数に対して作成したウォッチポイントを再度割り当てる必要があります。

ウォッチポイントを設定するには、次の要件を満たす必要があります。

  • ターゲットとなる物理デバイスやエミュレータが x86 CPU または x86_64 CPU を使用している必要があります。ARM CPU を使用しているデバイスの場合は、メモリ内の変数のアドレス境界を 4 バイト(32 ビット プロセッサの場合)または 8 バイト(64 ビット プロセッサの場合)に揃える必要があります。下記のように、変数の宣言で __attribute__((aligned(num_bytes))) を指定すると、ネイティブ コード内で変数を揃えることができます。
        // For a 64-bit ARM processor
        int my_counter __attribute__((aligned(8)));
        
  • すでに割り当て済みのウォッチポイントが 3 つ以下である必要があります。x86 または x86_64 のターゲット デバイスの場合、Android Studio がサポートするウォッチポイントの数は最大で 4 つです。他のデバイスの場合は、サポートされるウォッチポイントの数がそれよりも少ない可能性があります。

上記の要件を満たしている場合は、次の手順でウォッチポイントを追加できます。

  1. ブレークポイントでアプリが一時停止しているときに、LLDB セッション ビューで [Variables] パネルに移動します。
  2. トラッキングするメモリブロックを占有している変数を右クリックして、[Add Watchpoint] を選択します。ウォッチポイントを設定するためのダイアログが表示されます(図 7 を参照)。

    図 7: メモリの変数にウォッチポイントを追加する

  3. 以下のオプションを使用して、ウォッチポイントを設定します。
    • Enabled: しばらくこのウォッチポイントを無視するように Android Studio に指示する場合は、このオプションのチェックボックスをオフにします。ウォッチポイントは引き続き Android Studio 内で保存されるため、今後のデバッグ セッションでウォッチポイントにアクセスすることができます。
    • Suspend: デフォルトでは、Android システムは、ウォッチポイントに割り当てられたメモリブロックにアクセスするときに、アプリプロセスを一時停止します。この処理を変える場合は、このオプションのチェックボックスをオフにします。オフにすると、追加オプションが表示され、システムがウォッチポイントを処理する際の動作をカスタマイズできるようになります。追加オプションには、[Log message to console] と [Remove [the watchpoint] when hit] があります。
    • Access Type: 変数に割り当てられたメモリブロックに対してアプリが [Read] や [Write] を試行したときに、アプリがウォッチポイントをトリガーするかどうかを選択します。読み取りと書き込みのいずれでもウォッチポイントをトリガーするには、[Any] を選択します。
  4. [Done] をクリックします。

すべてのウォッチポイントを表示し、ウォッチポイント設定を指定するには、[Debug] ウィンドウの左側にある [View Breakpoints]()をクリックします。[Breakpoints] ダイアログが表示されます(図 8 を参照)。

図 8: 現在のすべてのウォッチポイントをリスト表示し、各ウォッチポイントの動作設定を表示する [Breakpoints] ダイアログ

ウォッチポイントを追加した後、[Debug] ウィンドウの左側にある [Resume Program]()をクリックすると、アプリプロセスを再開できます。デフォルトでは、ウォッチポイントを設定したメモリブロックに対してアプリがアクセスしようとすると、Android システムがアプリプロセスを一時停止し、アプリが最後に実行したコード行の横にウォッチポイント アイコン()が表示されます(図 9 を参照)。

図 9: ウォッチポイントがトリガーされる直前にアプリが実行したコード行を表示

リソース値を表示し、その表示形式を変更する

デバッグモードでは、Java コード内の変数を対象に、リソース値を表示したり、さまざまな表示形式を選択したりすることができます。[Variables] タブを表示して、フレームを選択し、次の手順を行います。

  1. [Variables] リストで、リソース行の任意の場所を右クリックして、プルダウン リストを表示します。
  2. プルダウン リストから、[View as] を選択して、使用する形式を選択します。

    使用できる形式は、選択したリソースのデータタイプによって異なります。以下のようなオプションが 1 つまたは複数表示されます。

    • Class: クラスの定義を表示します。
    • toString: 文字列形式を表示します。
    • Object: オブジェクト(クラスのインスタンス)の定義を表示します。
    • Array: 配列形式で表示します。
    • Timestamp: yyyy-mm-dd hh:mm:ss 形式で日時を表示します。
    • Auto: データタイプに基づいて Android Studio が最適な形式を選択します。
    • Binary: 0 と 1 を使用してバイナリ値を表示します。
    • MeasureSpec: 親から、選択した子に値が渡されます。MeasureSpec をご覧ください。
    • Hex: 16 進値として表示します。
    • Primitive: プリミティブ データタイプを使用して数値として表示します。
    • Integer: Integer 型の数値を表示します。

カスタム形式(データタイプ レンダラ)を作成するには:

  1. リソース値を右クリックします。
  2. [View as] を選択します。
  3. [Create] を選択します。[Java Data Type Renderers] ダイアログが表示されます。
  4. Java Data Type Renderers の手順に沿って設定します。