Android 11 デベロッパー プレビュー 2 が公開されました。ぜひお試しのうえ、フィードバックをお寄せください

アプリのデバッグ

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

  • デバイスを選択して、アプリをデバッグする。
  • Java、Kotlin、C / C++ のコード内にブレークポイントを設定する。
  • ランタイムにおける変数の検査や式の評価を行う。

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

デバッグを有効にする

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

  • 使用しているデバイスでデバッグを有効にします。

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

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

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

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

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

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

デバッグを開始する

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

  1. アプリコード内にブレークポイントを設定します。
  2. ツールバーで、ターゲット デバイスのプルダウン メニューからアプリをデバッグするデバイスを選択します。

    ターゲット デバイスのプルダウン メニュー

    構成済みのデバイスがない場合は、USB 経由でデバイスを接続するか、AVD を作成して Android Emulator を使用する必要があります。

  3. ツールバーの [Debug]ボタン をクリックします。

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

    その他の場合は、Android Studio で APK がビルドされます。その APK は、デバッグ鍵で署名された後に、選択したデバイスにインストールされ、実行されます。プロジェクトに C / C++ コードを追加した場合は、ネイティブ コードのデバッグのために、[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++ コードが含まれている場合は、自動的に Dual デバッグタイプが使用されます。そうでない場合は、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 とネイティブ コードの両方のデバッグを切り替えながら行う場合に指定します。 Java デバッガと LLDB の両方がアプリプロセス(1 つは Java デバッガ用、もう 1 つは 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. ブレークポイントを設定すると行の横に赤い丸が表示される

コードの実行がブレークポイントに達するとアプリは一時停止状態になります。その後、[Debugger] タブのツールを使用して、アプリの状態を確認できます。

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

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

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

  • メソッド呼び出しの 1 行目に進むには、[Step Into]()をクリックします。

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

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

プロジェクトでネイティブ コードを使用している場合は、デフォルトで Auto デバッグタイプが選択され、Java デバッガと LLDB の両方が 2 つの異なるプロセスとしてアプリにアタッチされます。そのため、アプリの再起動や設定の変更を行うことなく、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] ペインのプルダウン リストを使用することで、アプリプロセスにアクセスできます。各ペインについて詳しくは、ウィンドウ フレームをデバッグする変数を検査するのセクションをご覧ください。

    注: ネイティブ コード内のブレークポイントを検査している間、アプリの 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] ウィンドウの左側のリストで、各ブレークポイントの有効 / 無効を切り替えることができます。ブレークポイントを無効にすると、そのブレークポイントにヒットしてもアプリは一時停止しません。リストからブレークポイントを選択すると、その設定を行えます。最初はブレークポイントを無効にしておき、別のブレークポイントがヒットした後に有効になるよう設定できます。 また、ヒット後にブレークポイントを無効にするかどうかも設定できます。例外用のブレークポイントを設定するには、ブレークポイント リストで [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] ペインに追加することとは異なります。変数をこのペインに追加した場合は、変数の値をモニタリングすることはできますが、メモリ内の値が読み取られた場合や、変更された場合にアプリプロセスを一時停止させることはできません。

注: アプリプロセスで関数が終了し、ローカル変数に割り当てられていたメモリが解放されたら、その変数に対するウォッチポイントを再度割り当てる必要があります。

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

  • ターゲットの物理デバイスまたはエミュレータの CPU が x86 または x86_64 であること。デバイスの CPU が ARM の場合は、メモリ内の変数のアドレス境界を、32 ビット プロセッサでは 4 バイトの倍数、64 ビット プロセッサでは 8 バイトの倍数にそろえること。ネイティブ コード内で変数をこのようにそろえるには、次のように、変数宣言で __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: デフォルトでは、ウォッチポイントに割り当てられたメモリブロックがアクセスされると、アプリプロセスは一時停止します。 この処理を変える場合は、このオプションのチェックボックスをオフにします。オフにすると、追加オプションが表示され、システムによるウォッチポイントの処理動作をカスタマイズできるようになります。追加オプションには、[Log message to console] と [Remove [the watchpoint] when hit] があります。
    • Access Type: 変数に割り当てられたメモリブロックに対してアプリから [Read] または [Write] を試行したときに、ウォッチポイントをトリガーするかどうかを選択します。読み取りと書き込みのいずれでもウォッチポイントをトリガーするには、[Any] を選択します。
  4. [完了] をクリックします。

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

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

ウォッチポイントを追加した後、[Debug] ウィンドウの左側にある [Resume Program]()をクリックすると、アプリプロセスを再開できます。 デフォルトでは、ウォッチポイントを設定したメモリブロックに対してアプリからアクセスしようとすると、アプリプロセスが一時停止状態になり、アプリで最後に実行されたコード行の横にウォッチポイント アイコン が表示されます(図 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 を使用して 2 進値を表示します。
    • MeasureSpec: 親から、選択された子に渡された値です(MeasureSpec. を参照)。
    • Hex: 16 進値として表示します。
    • Primitive: プリミティブ型を使用して数値として表示します。
    • Integer: Integer 型の数値を表示します。

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

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