Depurar com pontos de interrupção

1. Antes de começar

Até este ponto, a maioria dos desenvolvedores iniciantes provavelmente já conheceu a depuração com log statements. Se você concluiu a Unidade 1, também sabe ler stack traces e pesquisar mensagens de erro. Embora ambos sejam ferramentas de depuração eficientes, os ambientes de desenvolvimento integrado modernos oferecem outras funções para melhorar ainda mais o processo.

Nesta lição, você vai aprender sobre o depurador integrado do Android Studio, como pausar a execução de um app e como executar linhas de código individualmente para identificar a origem exata do bug. Você também vai aprender a usar um recurso chamado Watches ("vigias") e monitorar variáveis específicas, em vez de precisar adicionar determinados log statements.

Pré-requisitos

  • Saber navegar em um projeto no Android Studio.
  • Entender a geração de registros em Kotlin.

O que você aprenderá

  • Como anexar o depurador ao app em execução.
  • Usar pontos de interrupção para pausar um app em execução e inspecionar o código, uma linha por vez.
  • Adicionar expressões condicionais a pontos de interrupção para economizar tempo na depuração.
  • Adicionar variáveis ao painel Watches para facilitar a depuração.

O que é necessário

  • Um computador com o Android Studio instalado.

2. Criar um novo projeto

Em vez de depurar um app grande e complexo, vamos começar com um projeto em branco e acrescentar um código com bugs para demonstrar as ferramentas de depuração no Android Studio.

Comece criando um novo projeto do Android Studio.

  1. Na tela Select a Project Template, selecione Blank Activity.

a949156bcfbf8a56.png

  1. Nomeie o app como Depuração. Verifique se a linguagem está definida como Kotlin e se todo o restante está inalterado.

9863157e10628a87.png

  1. Você verá um novo projeto do Android Studio que mostra um arquivo chamado MainActivity.kt.

e3ab4a557c50b9b0.png

Introduzir um bug

Você se lembra do exemplo de divisão por zero da lição de depuração na Unidade 1? Na iteração final da repetição, quando o app tenta fazer a divisão por zero, ele falha com uma java.langArithmeticException, porque a divisão por zero é impossível. Esse bug foi encontrado e corrigido examinando o stack trace, e essa suposição foi verificada usando log statements.

Como você já conhece esse exemplo, ele será usado para demonstrar como os pontos de interrupção podem ser usados. Os pontos de interrupção percorrem o código uma linha de cada vez sem a necessidade de adicionar log statements e executar novamente o app.

  1. Abra MainActivity.kt e substitua o código pelo seguinte:
package com.example.myapplication

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

public val TAG = "MainActivity"

class MainActivity : AppCompatActivity() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)
       division()
   }

   fun division() {
       val numerator = 60
       var denominator = 4
       repeat(5) {
           Log.v(TAG, "${numerator / denominator}")
           denominator--
       }
   }

}
  1. Execute o app e observe que ele falha conforme o esperado.

9468226e5f4d5729.png

3. Depurar com pontos de interrupção

Ao aprender sobre a geração de registros, você descobriu como eles podem ser estrategicamente posicionados para identificar bugs e verificar se eles foram corrigidos. Com bugs que você não introduziu, entretanto, nem sempre fica claro onde colocar os log statements ou quais variáveis exibir. Muitas vezes, você só encontra essas informações no momento da execução.

fun division() {
    val numerator = 60
    var denominator = 4
    repeat(5) {
        Log.v(TAG, "${numerator / denominator}")
        denominator--
    }
}

É aqui que entram os pontos de interrupção. Mesmo que você tenha uma vaga ideia do que está causando o bug com base nas informações do stack trace, é possível adicionar um ponto de interrupção que funcione como um sinal de parada para uma linha de código específica. Quando um ponto de interrupção é alcançado, ele pausa a execução. Assim, você pode usar outras ferramentas de depuração no momento da execução para entender melhor o que está acontecendo e o que deu errado.

Anexar o depurador

O Android Studio inclui uma ferramenta chamada Android Debug Bridge (adb). Essa é uma ferramenta de linha de comando integrada ao Android Studio que oferece recursos de depuração, como pontos de interrupção, aos apps que estão sendo executados. A ferramenta de depuração costuma ser chamada de depurador.

Para usar ou anexar o depurador em um app, você não pode simplesmente executar o app com Run > Run como antes. Em vez disso, execute o app com Run > Debug ‘app'.

21d706a854ebe710.png

Adicionar pontos de interrupção ao seu projeto

Siga as etapas abaixo para ver os pontos de interrupção em ação:

  1. Adicione um ponto de interrupção clicando no ícone de gutter ao lado do número da linha em que você quer pausar. Um ponto vai aparecer ao lado do número da linha, que será destacada.

6b6c2cd97bdc08ba.png

  1. Execute o app com o depurador anexado usando Run > Debug ‘app' ou o ícone f6a141c7f2a4e444.png na barra de ferramentas. Quando o app for iniciado, você verá uma tela como esta:

3bd9cbe69d5a0d0e.png

Depois que o app for iniciado, você verá o ponto de interrupção destacado quando ele for ativado.

a4860e59534f216a.png

Uma nova guia Debug será aberta na parte inferior da tela em que você visualizava a janela do Logcat.

ce37d2791db7302.png

À esquerda, há uma lista de funções, que são iguais às que aparecem no stack trace. À direita, há um painel em que é possível conferir os valores das variáveis individuais na função atual (por exemplo, division()). Na parte de cima, há também botões que ajudam você a navegar pelo programa enquanto ele está pausado. O botão que você vai usar com mais frequência é o Step Over, que executa a linha de código destacada.

a6c07c89e81abdc5.png

Siga as etapas a seguir para depurar o código:

  1. Depois que o ponto de interrupção for alcançado, a linha 19 (que declara a variável numerator) vai ficar destacada, mas ainda não terá sido executada. Use o botão Step Over 1d02d8134802ee64.png para executar a linha 19. Agora, a linha 20 vai ficar em destaque.

aa8f7063c836af0d.png

  1. Defina um ponto de interrupção na linha 22. Esse é o local em que a divisão ocorreu e é a linha em que o stack trace informou a exceção.

85758e1d4e69f9eb.png

  1. Use o botão Resume Program 8119afebc5492126.png à esquerda da janela Debug para ir até o próximo ponto de interrupção. Execute o restante da função division().

433d1c2a610b7945.png

  1. Observe que a execução é interrompida na linha 17 antes que ela seja executada.

2a16075287a03058.png

  1. Os valores de cada variável numerator e denominator são mostrados ao lado das declarações delas. Os valores das variáveis podem ser vistos na janela de depuração da guia Variables.

ebac20924bafbea5.png

  1. Pressione o botão Resume Program à esquerda da janela de depuração mais quatro vezes. Toda vez, a repetição é pausada e observa os valores de numerator e denominator. Na última iteração, numerator precisa ser 60 e denominator precisa ser 0. E não é possível dividir 60 por 0.

246dd310b7fb54fe.png

Agora, você sabe a linha de código exata que causa o bug e sabe o motivo. Como antes, é possível corrigir o bug mudando o número de repetições do código de 5 para 4.

fun division() {
    val numerator = 60
    var denominator = 4
    repeat(4) {
        Log.v(TAG, "${numerator / denominator}")
        denominator--
    }
}

4. Definir condições para pontos de interrupção

Na seção anterior, era preciso percorrer cada iteração da repetição até que o denominador fosse zero. Em apps mais complicados, isso pode ser trabalhoso quando você tem menos informações sobre o bug. No entanto, se você presumir, por exemplo, que o app só falha quando o denominador é zero, é possível modificar o ponto de interrupção para que ele seja alcançado somente quando essa suposição for atendida, em vez de ter que percorrer cada iteração da repetição.

  1. Se necessário, reintroduza o bug mudando 4 para 5 na repetição.
repeat(4) {
    ...
}
  1. Coloque um novo ponto de interrupção na linha com a instrução repeat.

2da539348a57af97.png

  1. Clique com o botão direito do mouse no ícone vermelho do ponto de interrupção. Um menu será mostrado com algumas opções, por exemplo, se o ponto de interrupção ficará ativado ou não. Um ponto de interrupção desativado ainda existe, mas não é acionado no momento da execução. Você também tem a opção de adicionar uma expressão Kotlin que, se for avaliada como verdadeira, vai acionar o ponto de interrupção. Por exemplo, se você usar a expressão denominator > 3, o ponto de interrupção só será acionado na primeira iteração da repetição. Para acionar o ponto de interrupção apenas quando o app puder ser dividido por zero, defina a expressão como denominator == 0. As opções do ponto de interrupção precisam ser semelhantes a estas:

890b999988aef59b.png

  1. Execute o app usando Run > Debug 'app' e observe se o ponto de interrupção foi alcançado.

482ec7c2a23040ef.png

Veja que o denominador já é 0. O ponto de interrupção só foi acionado quando a condição foi atendida, economizando tempo e esforço para percorrer o código.

9f4e29b5a91fdb12.png

  1. Como antes, o bug é causado pela repetição que executa um único ponto várias vezes, em que o denominador foi definido como 0.

Adicionar vigias

Se você quiser monitorar um valor específico durante a depuração, não será necessário pesquisar na guia Variables para encontrá-lo. Você pode adicionar Watches para monitorar variáveis específicas. Essas variáveis vão ficar visíveis no painel de depuração. Quando a execução for pausada e essa variável estiver no escopo, ela ficará visível no painel Watches. Isso torna a depuração mais eficiente ao trabalhar com projetos maiores. Você poderá acompanhar todas as variáveis relevantes em um só lugar.

  1. Na visualização de depuração, à direita do painel de variáveis, haverá outro painel vazio chamado Watches. Clique no botão de adição d6fb21f8b9c67448.png no canto superior esquerdo. Talvez você veja a opção de menu New Watch.

990d62a98ec9ba5b.png

  1. Digite o nome da variável (denominator) no campo fornecido e clique em "Enter".
  2. Execute o app novamente com Run > debug 'app' e observe que, quando o ponto de interrupção for alcançado, você verá o valor do denominador no painel Watches.

5. Parabéns

Resumindo:

  • É possível definir pontos de interrupção para pausar a execução do seu app.
  • Quando a execução estiver pausada, você poderá usar "step over" para executar uma única linha de código.
  • É possível definir instruções condicionais para só acionar pontos de interrupção com base em uma expressão Kotlin.
  • Os Watches (vigias) permitem agrupar variáveis de interesse ao depurar.

Saiba mais