Depurar seu app

O Android Studio oferece um depurador que permite fazer o seguinte e muito mais:

  • Selecionar um dispositivo no qual depurar seu app.
  • Definir pontos de interrupção no seu código Java, Kotlin e C/C++.
  • Examinar as variáveis e avaliar as expressões no tempo de execução.

Esta página inclui instruções para operações básicas do depurador. Para ver outras documentações, consulte também os documentos de depuração IntelliJ IDEA (link em inglês).

Ativar depuração

Antes de começar a depuração, você precisa se preparar da seguinte maneira:

  • Ative a depuração no seu dispositivo:

    Se você estiver usando o emulador, ela estará ativada por padrão. No entanto, em um dispositivo conectado, é necessário ativar a depuração nas opções do desenvolvedor no dispositivo.

  • Execute uma variante de compilação depurável:

    Você precisa usar uma variante de compilação que inclua debuggable true (link em inglês) na configuração da compilação. Normalmente, é possível selecionar a variante padrão "debug" incluída em todo projeto do Android Studio, mesmo que não esteja visível no arquivo build.gradle. Porém, se você definir novos tipos de build que precisam ser depuráveis, será necessário adicionar "debuggable true" ao tipo de build.

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

    Essa propriedade também se aplica a módulos com código C/C++. A propriedade jniDebuggable não é mais usada.

    Caso seu app dependa de um módulo de biblioteca que você também queira depurar, a biblioteca também precisará ser empacotada com debuggable true para que ela retenha os símbolos de depuração. Para garantir que as variantes depuráveis do seu projeto de app recebam a variante depurável de um módulo de biblioteca, publique versões não padrão da sua biblioteca.

Começar a depurar

É possível começar uma sessão de depuração da seguinte maneira:

  1. Defina alguns pontos de interrupção no código do app.
  2. Na barra de ferramentas, selecione um dispositivo para depurar seu aplicativo no menu suspenso do dispositivo de destino.

    Menu suspenso do dispositivo de destino.

    Caso você não tenha nenhum dispositivo configurado, será necessário conectar um dispositivo via USB ou criar um AVD para usar o Android Emulator.

  3. Na barra de ferramentas, clique em Debug .

    Se uma caixa de diálogo perguntando se você quer "switch from Run to Debug" for exibida, isso significa que seu app já está sendo executado no dispositivo e será reiniciado para começar a depuração. Caso prefira manter a mesma instância do app em execução, clique em Cancel Debug e anexe o depurador a um app em execução.

    Caso contrário, um APK será criado, assinado com uma chave de depuração, instalado no dispositivo selecionado e executado pelo Android Studio. Se você adicionar um código C e C++ ao seu projeto, o Android Studio também executará o depurador LLDB (link em inglês) na janela Debug para depurar seu código nativo.

  4. Se a janela Debug não estiver aberta, selecione View > Tool Windows > Debug ou clique em Debug na barra da janela de ferramentas e depois na guia Debugger, conforme mostrado na Figura 1.

    Figura 1. Janela "Debugger", mostrando a linha de execução atual e a árvore de objetos de uma variável.

Anexar o depurador a um app em execução

Se o app já estiver em execução no dispositivo, você poderá começar a depuração sem reiniciá-lo:

  1. Clique em Attach debugger to Android process .
  2. Na caixa de diálogo Choose Process, selecione o processo a que você quer conectar o depurador.

    Se estiver usando um emulador ou um dispositivo com acesso root, selecione Show all processes para ver todos os processos.

    No menu suspenso Debugger, é possível selecionar outro tipo de depuração. Por padrão, o Android Studio usa o tipo de depuração Auto para selecionar a melhor opção de depurador para você com base na presença de código Java ou C/C++ no seu projeto.

  3. Clique em OK.

    A janela Debug será exibida.

Observação: o depurador e o coletor de lixo do Android Studio são relativamente integrados. A máquina virtual do Android garante que qualquer objeto que o depurador conheça não seja coletado até que o depurador seja desconectado. Isso pode resultar em um acúmulo de objetos ao longo do tempo, enquanto o depurador estiver conectado. Por exemplo, se o depurador detectar uma linha de execução em funcionamento, o objeto Thread associado não será coletado até que o depurador seja desconectado, mesmo se a linha de execução for encerrada.

Mudar o tipo de depurador

Como diferentes ferramentas do depurador são necessárias para depurar código Java/Kotlin e código C/C++, o depurador do Android Studio permite selecionar o tipo de depurador a ser usado. Por padrão, o Android Studio decide qual depurador usar pelas linguagens do seu projeto detectados com o tipo de depurador Auto. No entanto, é possível selecionar manualmente o depurador na configuração de depuração. Clique em Run > Edit Configurations ou na caixa de diálogo que aparece quando você clica em Run > Attach debugger to Android process.

Os tipos de depuração disponíveis incluem:

Auto
Selecione esse tipo de depuração caso queira que o Android Studio escolha automaticamente a melhor opção para o código sendo depurado. Por exemplo, caso seu app inclua código C ou C++, o Android Studio usará o tipo de depuração Dual automaticamente. Caso contrário, o Android Studio usará o tipo de depuração Java.
Java
Selecione esse tipo de depuração caso queira depurar somente o código em Java ou Kotlin. O depurador Java ignora todos os pontos de interrupção ou controle definidos no código nativo.
Native (disponível apenas para código C/C++)
Selecione esse tipo de depuração caso queira usar apenas o LLDB para depurar seu código. Ao usar esse tipo de depuração, a visualização da sessão do depurador Java não estará disponível. Por padrão, o LLDB inspeciona apenas seu código nativo e ignora os pontos de interrupção no seu código Java. Caso você também queira depurar o código Java, use o tipo de depuração Auto ou Dual.

O modo de depuração "Native" só funciona em dispositivos que atendem aos seguintes requisitos:

  • O dispositivo é compatível com run-as.

    Para verificar se o dispositivo é compatível com run-as, execute o seguinte comando no shell do ADB conectado ao dispositivo:

    run-as your-package-name pwd
    

    Substitua your-package-name pelo nome do pacote do seu app. Se o dispositivo for compatível com run-as, o comando retornará sem erros.

  • O ptrace está ativado no dispositivo.

    Para verificar se o ptrace está ativado, execute o seguinte comando no shell do ADB conectado ao dispositivo:

    sysctl kernel.yama.ptrace_scope
    

    Se ptrace estiver ativado, o comando exibirá o valor 0 ou um erro unknown key. Se ptrace não estiver ativado, um valor diferente de 0 será exibido.

Dual (disponível apenas para código C/C++)
Selecione esse tipo de depuração se quiser alternar entre a depuração de código nativo e Java. O Android Studio conecta os depuradores Java e LLDB ao processo do seu app. Assim, é possível inspecionar os pontos de interrupção no código Java e no código nativo sem reiniciar o app ou mudar a configuração de depuração.

Na Figura 2, observe as duas guias à direita do título da janela Debug. Como o app tem código Java e C++, uma guia serve para depurar o código nativo e a outra para o código Java, conforme indicado por -java.

Figura 2. Guia para depuração de código nativo e guia para depuração de código Java.

Observação: caso esteja depurando código nativo otimizado pelo compilador, você poderá receber a seguinte mensagem de aviso: This function was compiled with optimizations enabled. Some debugger features may not be available. Ao usar sinalizações de otimização, como sinalizações -O, o compilador muda seu código compilado para torná-lo mais eficiente. Isso pode fazer com que o depurador passe informações inesperadas e incorretas, já que é difícil para o depurador mapear o código compilado otimizado de volta para o código-fonte original. Por esse motivo, ao depurar seu código nativo, desative as otimizações de compilador.

Usar o registro do sistema

O registro do sistema mostra mensagens do sistema enquanto você depura seu app. Elas incluem informações sobre os apps em execução no dispositivo. Caso queira usar o registro do sistema para depurar seu app, seu código precisa gravar mensagens de registro e exibir o stack trace para exceções enquanto o app estiver na fase de desenvolvimento.

Gravar mensagens de registro no código

Para gravar mensagens de registro no seu código, use a classe Log. As mensagens de registro ajudam você a entender o fluxo de execução ao coletar a saída de depuração do sistema enquanto você interage com seu app. Elas podem informar qual parte do app apresenta falhas. Para saber mais sobre registros, consulte Gravar e ver registros.

O exemplo a seguir mostra como adicionar mensagens de registro para determinar se há informações de estado anterior disponíveis quando sua atividade for iniciada:

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 */
        }
    }
}

Durante o desenvolvimento, seu código também pode identificar exceções e gravar o stack trace no registro do sistema:

Kotlin

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

Java

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

Observação: remova as mensagens de registro de depuração e as chamadas de exibição de stack trace do seu código quando o app estiver pronto para ser publicado. Isso pode ser feito definindo uma sinalização DEBUG e inserindo mensagens de registro de depuração em declarações condicionais.

Ver o registro do sistema

É possível ver e filtrar mensagens de depuração e outras mensagens de sistema na janela Logcat. Por exemplo, é possível ver mensagens quando a coleta de lixo ocorre ou ver as mensagens adicionadas ao app com a classe Log.

Para usar o logcat, inicie a depuração e selecione a guia Logcat na barra de ferramentas inferior, conforme mostrado na Figura 3.

Figura 3. Janela do Logcat com configurações de filtro

Consulte Gravar e visualizar registros com o Logcat para ver descrições do logcat e as opções de filtragem.

Trabalhar com pontos de interrupção

O Android Studio é compatível com diversos tipos de pontos de interrupção que acionam diferentes ações de depuração. O tipo mais comum é um ponto de interrupção de linha que interrompe a execução do seu app quando ele chega a uma linha de código especificada. Durante a pausa, é possível examinar variáveis, avaliar expressões e continuar a execução linha por linha para determinar as causas de erros no ambiente de execução.

Para adicionar um ponto de interrupção de linha, faça o seguinte:

  1. Localize a linha de código em que você quer pausar a execução e clique na margem esquerda dessa linha ou posicione o cursor na linha e pressione Ctrl+F8 (em computadores Mac, pressione Command+F8).
  2. Caso seu app já esteja em execução, não será preciso atualizá-lo para adicionar o ponto de interrupção. Clique em Attach debugger to Android process . Caso contrário, clique em Debug para iniciar a depuração.

Figura 3. Um ponto vermelho é exibido ao lado da linha depois de definir um ponto de interrupção.

Quando a execução do seu código atinge o ponto de interrupção, o Android Studio pausa a execução do app. Depois disso, é possível usar as ferramentas na guia Debugger para identificar o estado do app.

  • Para examinar a árvore de objetos de uma variável, expanda-a na visualização Variables. Se a visualização Variables não estiver disponível, clique em Restore Variables View .

  • Para avaliar uma expressão no ponto de execução atual, clique em Evaluate Expression .

  • Para avançar para a próxima linha do código sem inserir um método, clique em Step Over .

  • Para avançar para a primeira linha dentro de uma chamada de método, clique em Step Into .

  • Para avançar para a próxima linha fora do método atual, clique em Step Out .

  • Para continuar executando o app normalmente, clique em Resume Program .

Caso seu projeto use código nativo, por padrão, o tipo de depuração Auto conectará o depurador Java e o LLDB ao seu app como dois processos separados. Assim, você pode alternar entre inspecionar os pontos de interrupção Java e C/C++ sem reiniciar o app nem mudar as configurações.

Observação: para que o Android Studio detecte pontos de interrupção no seu código C ou C++, use um tipo de depuração compatível com o LLDB, como Auto, Native ou Dual. Mude o tipo de depuração usado pelo Android Studio editando sua configuração de depuração. Para saber mais sobre os diferentes tipos de depuração, leia a seção sobre como usar outros tipos de depuração.

Quando o Android Studio implanta seu app no dispositivo de destino, a janela "Debug" é exibida com uma guia ou com uma visualização de sessão de depuração para cada processo de depuração, conforme mostrado na Figura 4.

Figura 4. Como depurar código nativo com LLDB.

  1. O Android Studio alterna para a guia <your-module> quando o depurador LLDB encontra um ponto de interrupção no código C/C++. Os painéis Frames, Variables e Watches também estão disponíveis e funcionam exatamente como funcionariam se você estivesse depurando código Java. Embora o painel Threads não esteja disponível na visualização da sessão do LLDB, é possível acessar os processos do app com a lista suspensa do painel Frames. Saiba mais sobre esses painéis nas seções sobre como depurar frames de janela e inspecionar variáveis.

    Observação: ao inspecionar um ponto de interrupção no seu código nativo, o sistema Android suspende a máquina virtual que executa o bytecode Java do app. Isso significa que não é possível interagir com o depurador Java nem recuperar informações de estado da sessão de depuração Java ao inspecionar um ponto de interrupção no código nativo.

  2. O Android Studio alterna para a guia <your-module>-java quando o depurador do Java encontra um ponto de interrupção no código Java.
  3. Ao depurar com LLDB, você pode usar o terminal do LLDB na visualização da sessão para passar opções de linha de comando para o LLDB (link em inglês). Se quiser que o LLDB execute comandos específicos sempre que você iniciar a depuração do app, antes ou depois de conectar o depurador ao processo do seu app, você pode adicionar esses comandos à configuração de depuração.

Enquanto você depura o código C/C++, também é possível definir tipos especiais de pontos de interrupção, chamados pontos de controle, que podem suspender o processo do seu app quando ele interage com um bloco de memória específico. Para saber mais, leia a seção sobre como adicionar pontos de controle.

Ver e configurar pontos de interrupção

Para ver todos os pontos de interrupção e definir as configurações, clique em View Breakpoints no lado esquerdo da janela Debug. A janela Breakpoints será exibida, conforme mostrado na Figura 5.

Figura 5. A janela "Breakpoints" lista todos os pontos de interrupção atuais e inclui as configurações de comportamento de cada um.

A janela Breakpoints permite ativar ou desativar cada ponto de interrupção da lista à esquerda. Se um ponto de interrupção for desativado, o Android Studio não pausará seu app quando ele o alcançar esse ponto. Selecione um ponto de interrupção na lista para definir as configurações. É possível configurar um ponto de interrupção para ser desativado inicialmente e fazer com que o sistema o ative depois que outro ponto de interrupção for alcançado. Também é possível configurar se um ponto de interrupção precisa ser desativado depois de ser alcançado. Para definir um ponto de interrupção para qualquer exceção, selecione Exception Breakpoints na lista de pontos de interrupção.

Depurar frames de janela

Na janela Debugger, o painel Frames permite inspecionar o frame da pilha que fez com que o ponto de interrupção atual fosse alcançado. Assim, é possível navegar e examinar o frame da pilha e inspecionar a lista de linhas de execução no seu app para Android. Para selecionar uma linha de execução, use a lista suspensa do seletor e veja o frame da pilha. Clicar nos elementos do frame abre a fonte no editor. Também é possível personalizar a apresentação da linha de execução e exportar o frame da pilha conforme discutido no Guia de frames de janela (link em inglês).

Inspecionar variáveis

Na janela Debugger, o painel Variables permite inspecionar variáveis quando o sistema interrompe seu app em um ponto de interrupção e você seleciona um frame no painel Frames. O painel Variables também permite avaliar expressões ad-hoc usando métodos e/ou variáveis estáticos disponíveis no frame selecionado.

O painel Watches oferece recursos semelhantes. Contudo, as expressões adicionadas ao painel Watches persistem entre sessões de depuração. Adicione pontos de controle para variáveis e campos que você acessa com frequência ou que oferecem estados úteis para a sessão de depuração atual. Os painéis Variables e Watches são exibidos conforme mostrado na Figura 5.

Para adicionar uma variável ou expressão à lista Watches, siga estas etapas:

  1. Inicie a depuração.
  2. No painel Watches, clique em Add .
  3. Na caixa de texto exibida, digite o nome da variável ou da expressão que você quer controlar e pressione Enter.

Para remover um item da lista Watches selecione-o e clique em Remove .

Para reorganizar os elementos na lista Watches, selecione um item e clique em Up ou Down .

Figura 6. Os painéis "Variables" e "Watches" na janela "Debugger".

Adicionar pontos de controle

Durante a depuração do código C/C++, é possível definir tipos especiais de pontos de interrupção chamados pontos de controle, que suspendem o processo do app quando ele interage com um bloco de memória específico. Por exemplo: se você definir dois ponteiros para um bloco de memória e atribuir um ponto de controle a ele, o uso de qualquer um desses ponteiros para acessar esse bloco de memória acionará o ponto de controle.

No Android Studio, é possível selecionar uma variável específica para criar um ponto de controle no ambiente de execução. Entretanto, o LLDB atribui o ponto de controle apenas para o bloco de memória que o sistema aloca para a variável, não para a variável em si. Isso é diferente de adicionar uma variável ao painel Watches, o que permite observar o valor de uma variável, mas não suspender o processo do app quando o sistema ler ou alterar o valor dele na memória.

Observação: quando o processo do app sai de uma função e o sistema desaloca as variáveis locais da memória, é necessário reatribuir todos os pontos de controle criados para essas variáveis.

Para definir um ponto de controle, é preciso atender aos seguintes requisitos:

  • O dispositivo ou emulador de destino precisa usar uma CPU de x86 ou x86_64. Se o dispositivo usar uma CPU ARM, será necessário alinhar o limite do endereço da variável na memória como 4 bytes para processadores de 32 bits ou 8 bytes para processadores de 64 bits. Especifique __attribute__((aligned(num_bytes))) na desaceleração da variável para alinhar uma variável no seu código nativo, conforme mostrado abaixo:
    // For a 64-bit ARM processor
    int my_counter __attribute__((aligned(8)));
    
  • Você já alinhou três pontos de controle ou menos. O Android Studio aceita até quatro pontos de controle em dispositivos de destino de x86 ou x86_64. Outros dispositivos podem aceitar menos pontos de controle.

Observação: ao depurar seu app com ABIs ARM de 32 bits, adicionar um ponto de controle ou passar o cursor sobre as variáveis no código para ver os valores delas pode causar uma falha. Como solução alternativa, faça a depuração usando binários ARM, x86 ou x86_64 de 64 bits. Esse problema será corrigido em uma versão futura do Android Studio.

Se você atender aos requisitos acima, poderá adicionar um ponto de controle da seguinte maneira:

  1. Enquanto seu app estiver suspenso em um ponto de interrupção, acesse o painel Variables na visualização da sua sessão do LLDB.
  2. Clique com o botão direito do mouse em uma variável que ocupa o bloco de memória que você quer rastrear e selecione Add Watchpoint. Uma caixa de diálogo para configurar seu ponto de controle será exibida, conforme mostrado na Figura 7.

    Figura 7. Adição de um ponto de controle a uma variável na memória

  3. Configure seu ponto de controle com as seguintes opções:
    • Enabled: desmarque essa opção caso queira que o Android Studio ignore o ponto de controle por enquanto. O Android Studio ainda salvará seu ponto de controle para que seja possível acessá-lo posteriormente na sessão de depuração.
    • Suspend: por padrão, o sistema Android suspende o processo do seu app quando ele acessa um bloco de memória atribuído a um ponto de controle. Desmarque essa opção caso não queira que isso aconteça. Fazer isso revela outras opções que podem ser úteis para personalizar o comportamento do sistema quando ele interage com um ponto de controle: Log message to console e Remove [the watchpoint] when hit.
    • Access Type: selecione essa opção caso seu app precise acionar seu ponto de controle quando tentar ler ou gravar no bloco de memória que o sistema alocar para a variável. Para acionar o ponto de controle em uma ação de leitura ou gravação, selecione Any.
  4. Clique em Done.

Para ver todos os pontos de controle e definir as configurações, clique em View Breakpoints no lado esquerdo da janela Debug. A caixa de diálogo Breakpoints será exibida, conforme mostrado na Figura 8.

Figura 8. A caixa de diálogo "Breakpoints" lista os pontos de controle atuais e inclui as configurações de comportamento de cada um.

Depois de adicionar um ponto de controle, clique em Resume Program no lado esquerdo da janela Debug para retomar o processo do seu app. Por padrão, se o app tentar acessar um bloco de memória para o qual você atribuiu um ponto de controle, o sistema Android suspenderá o processo do app e um ícone de ponto de controle será exibido ao lado da última linha de código executada, conforme mostrado na Figura 9.

Figura 9. O Android Studio indica a linha de código executada pelo app pouco antes de um ponto de controle ser acionado.

Ver e mudar o formato de exibição dos valores de recursos

No modo de depuração, é possível ver os valores dos recursos e selecionar um formato de exibição diferente para variáveis no seu código Java. Com a guia Variables exibida e um frame selecionado, faça o seguinte:

  1. Na lista Variables, clique com o botão direito do mouse em qualquer lugar de uma linha de recurso para exibir a lista suspensa.
  2. Na lista suspensa, selecione View as e o formato que você quer usar.

    Os formatos disponíveis dependem do tipo de dado do recurso selecionado. Você poderá ver qualquer uma ou mais das seguintes opções:

    • Class: exibe a definição da classe.
    • toString: exibe o formato da string.
    • Object: exibe a definição de objeto (uma instância de uma classe).
    • Array: exibe em um formato de matriz.
    • Timestamp: exibe a data e a hora como aaaa-mm-dd hh:mm:ss.
    • Auto: o Android Studio escolhe o melhor formato com base no tipo de dado.
    • Binary: exibe um valor binário usando 0 e 1.
    • MeasureSpec: o valor passado do pai para o filho selecionado. Consulte MeasureSpec.
    • Hex: exibe como um valor hexadecimal.
    • Primitive: exibe como um valor numérico usando um tipo de dado primitivo.
    • Integer: exibe um valor numérico do tipo Integer.

Você pode criar um formato personalizado (renderizador de tipo de dado) da seguinte maneira:

  1. Clique no valor do recurso com o botão direito do mouse.
  2. Selecione View as.
  3. Selecione Create. A caixa de diálogo Java Data Type Renderers será exibida.
  4. Siga as instruções em Renderizadores de tipos de dados Java (link em inglês).