Cómo realizar depuraciones con puntos de interrupción

1. Antes de comenzar

Hasta este punto, es probable que la mayoría de los desarrolladores principiantes estén al tanto de la depuración mediante instrucciones de registro. Si completaste la Unidad 1, ya sabes cómo leer seguimientos de pila e investigar mensajes de error. Si bien ambas son herramientas de depuración potentes, los IDE modernos proporcionan más funcionalidades para hacer que el proceso de depuración sea más eficaz.

En esta lección, aprenderás sobre el depurador integrado de Android Studio, cómo pausar la ejecución de una app y cómo ejecutar líneas de código únicas a la vez para identificar la fuente exacta del error. También aprenderás a usar una función llamada Watches y harás un seguimiento de variables específicas en lugar de tener que agregar instrucciones de registro específicas.

Requisitos previos

  • Debes saber cómo navegar por un proyecto en Android Studio.
  • Debes tener conocimientos de registros en Kotlin.

Qué aprenderás

  • Cómo adjuntar el depurador a tu app en ejecución
  • Cómo usar los puntos de interrupción para pausar una app en ejecución e inspeccionar el código de a una línea por vez
  • Cómo agregar expresiones condicionales a los puntos de interrupción para ahorrar tiempo en la depuración
  • Cómo agregar variables al panel Watches para facilitar la depuración

Requisitos

  • Una computadora que tenga Android Studio instalado

2. Crea un proyecto nuevo

En lugar de depurar una app grande y compleja, comenzaremos con un proyecto en blanco y agregaremos un código con errores para que podamos conocer las herramientas de depuración en Android Studio.

Para comenzar, crea un nuevo proyecto de Android Studio.

  1. En la pantalla Select a Project Template, elige Blank Activity.

a949156bcfbf8a56.png

  1. Asígnele el nombre Debugging a la aplicación, asegúrate de que el lenguaje esté configurado en Kotlin y que no haya cambios.

9863157e10628a87.png

  1. Aparecerá un nuevo proyecto de Android Studio en el que se mostrará un archivo llamado MainActivity.kt.

e3ab4a557c50b9b0.png

Cómo ingresar un error

¿Recuerdas el ejemplo de la división por cero de la clase de depuración en la Unidad 1? En la iteración final del bucle, cuando la app intenta realizar la división por cero, falla con una java.langArithmeticException, ya que es imposible dividir por cero. Ese error se encontró y se corrigió examinando el seguimiento de pila, y esta suposición se verificó con instrucciones de registro.

Como ya conoces ese ejemplo, se usará para demostrar cómo se pueden usar los puntos de interrupción. Los puntos de interrupción sirven para revisar el código de a una línea a la vez, sin necesidad de agregar instrucciones de registro y volver a ejecutar la app.

  1. Abre MainActivity.kt y reemplaza el código por lo siguiente:
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. Ejecuta la app. Observa que falla según lo previsto.

9468226e5f4d5729.png

3. Cómo realizar depuraciones con puntos de interrupción

Cuando aprendiste sobre el registro, aprendiste a colocar de manera estratégica los registros para ayudar a identificar errores y verificar que se hayan corregido. Sin embargo, cuando te encuentras con errores que no introdujiste, no siempre está claro dónde colocar las instrucciones de registro o qué variables se deben mostrar. A menudo, solo puedes encontrar esa información durante el tiempo de ejecución.

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

Aquí es donde entran en juego los puntos de interrupción. Incluso si tienes una idea clara de lo que causa el error en función de la información en el seguimiento de pila, puedes agregar un punto de interrupción que funcione como una señal de alto para una línea de código específica. Una vez que se alcanza un punto de interrupción, se detiene la ejecución, lo que te permite usar otras herramientas de depuración durante el tiempo de ejecución para ver de cerca lo que está sucediendo y lo que salió mal.

Cómo adjuntar el depurador

En segundo plano, Android Studio usa una herramienta llamada Android Debug Bridge, también conocida como ADB. Es una herramienta de línea de comandos integrada en Android Studio que proporciona capacidades de depuración (como puntos de interrupción) a tus apps en ejecución. Una herramienta de depuración suele recibir el nombre de depurador.

Para usar o adjuntar el depurador en una app, no puedes simplemente ejecutar la app con Run > Run como antes. En su lugar, puedes ejecutarla con Run > Debug "app".

21d706a854ebe710.png

Cómo agregar puntos de interrupción a tu proyecto

Sigue estos pasos para ver los puntos de interrupción en acción:

  1. Para agregar un punto de interrupción, haz clic en el margen junto al número de línea en el que deseas pausar. Aparecerá un punto junto al número de línea y esta se destacará.

6b6c2cd97bdc08ba.png

  1. Ejecuta tu app con el depurador. Para ello, usa Run > Debug "app" o el ícono f6a141c7f2a4e444.png de la barra de herramientas. Cuando se inicie la app, deberías ver una pantalla como esta.

3bd9cbe69d5a0d0e.png

Una vez que se inicie la app, verás el punto de interrupción destacado cuando esté activada.

a4860e59534f216a.png

En la parte inferior de la pantalla, donde anteriormente se veía la ventana de Logcat, se abrió una nueva pestaña de Debug.

ce37d2791db7302.png

En la parte izquierda, hay una lista de funciones, que son las mismas que las que aparecieron en el seguimiento de pila. En el lado derecho, hay un panel en el que puedes verificar los valores de variables individuales en la función actual (es decir, division()). En la parte superior, también hay botones que te permiten navegar por el programa mientras está pausado. El que más utilizarás es el de Step Over, que ejecuta la única línea de código destacada.

a6c07c89e81abdc5.png

Realiza los siguientes pasos para depurar el código:

  1. Después de llegar al punto de interrupción, la línea 19 (declarando la variable numerator) ahora está destacada, pero aún no se ejecutó. Usa el botón Step Over 1d02d8134802ee64.png para ejecutar la línea 19. Ahora, se destacará la línea 20.

aa8f7063c836af0d.png

  1. Establece un punto de interrupción en la línea 22. Aquí es donde se produjo la división, y es la línea en la que el seguimiento de pila informó la excepción.

85758e1d4e69f9eb.png

  1. Usa el botón Resume Program 8119afebc5492126.png que se encuentra a la izquierda de la ventana Debug para ir al siguiente punto de interrupción y ejecutar el resto de la función division().

433d1c2a610b7945.png

  1. Observa que la ejecución se detiene en la línea 17 antes de ejecutarla.

2a16075287a03058.png

  1. Los valores de cada variable (numerator y denominator) se muestran junto a sus declaraciones. Los valores de las variables se pueden ver en la ventana de depuración de la pestaña Variables.

ebac20924bafbea5.png

  1. Presiona el botón Resume Program ubicado cuatro veces más a la izquierda de la ventana de depuración cada vez que el bucle se detenga y se observen los valores de numerator y denominator. En la última iteración, numerator debe ser 60, y denominator debe ser 0. Además, no se puede dividir el número 60 por 0.

246dd310b7fb54fe.png

Ahora, conoces la línea de código exacta que causa el error y el motivo exacto. Como antes, puedes corregir el error cambiando la cantidad de veces que deseas que se repita el código de 5 a 4.

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

4. Cómo establecer condiciones para los puntos de interrupciones

En la sección anterior, debías avanzar por cada iteración del bucle hasta que el denominador fuera cero. En apps más complicadas, esto puede ser tedioso cuando tienes menos información sobre el error. Sin embargo, si tienes una suposición, por ejemplo, que la app solo falla cuando el denominador es cero, puedes modificar el punto de interrupción para que solo se alcance cuando se cumpla esta suposición, en lugar de tener que revisar cada iteración del bucle.

  1. Si es necesario, vuelve a introducir el error. Para ello, cambia 4 por 5 en el bucle de repetición.
repeat(4) {
    ...
}
  1. Coloca un nuevo punto de interrupción en la línea con la declaración repeat.

2da539348a57af97.png

  1. Haz clic con el botón derecho en el ícono del punto de interrupción rojo. Aparecerá un menú con algunas opciones, por ejemplo, si se habilitó o no el punto de interrupción. Todavía existe un punto de interrupción inhabilitado, pero no se activará durante el tiempo de ejecución. También tienes la opción de agregar una expresión de Kotlin que, si se evalúa como verdadera, se activará el punto de interrupción. Por ejemplo, si usaste la expresión denominator > 3, el punto de interrupción solo se activará en la primera iteración del bucle. Para activar solo el punto de interrupción cuando tu app pueda dividirse por cero, configura la expresión en denominator == 0. Las opciones para el punto de interrupción deberían verse de la siguiente manera:

890b999988aef59b.png

  1. Ejecuta tu app con Run > Debug 'app' y observa que se alcance el punto de interrupción.

482ec7c2a23040ef.png

Puedes ver que el denominador ya es 0. El punto de interrupción solo se activó cuando se cumplió con la condición, lo que te ahorra tiempo y esfuerzo para recorrer el código línea a línea.

9f4e29b5a91fdb12.png

  1. Como antes, ves que el error se debe a la correa que se ejecuta demasiadas veces, donde el denominador se estableció en 0.

Cómo agregar Watches

Si quieres supervisar un valor específico durante la depuración, no necesitas buscar en la pestaña Variables para encontrarlo. Puedes agregar algo llamado Watches para supervisar variables específicas, que serán visibles en el panel de depuración. Cuando se pause la ejecución y esa variable esté dentro del alcance, esta se verá en el panel Watches. Esto hace que la depuración sea más eficiente cuando trabajas con proyectos más grandes. Podrás hacer un seguimiento de todas las variables relevantes en un solo lugar.

  1. En la vista de depuración, a la derecha del panel de variables, debería haber otro panel vacío llamado Watches. Haz clic en el botón de signo más d6fb21f8b9c67448.png ubicado en la esquina superior izquierda. Es posible que veas una opción de menú que diga New Watch.

990d62a98ec9ba5b.png

  1. Escribe el nombre de la variable denominator en el campo proporcionado y haz clic en Intro.
  2. Vuelve a ejecutar tu app con Run > debug 'app' y observa que, cuando se alcance el punto de interrupción, verás el valor del denominador en el panel Watches.

5. Felicitaciones

Resumen:

  • Puedes establecer interrupciones para pausar la ejecución de tu app.
  • Cuando se detiene la ejecución, puedes usar "step over" para ejecutar solo una línea de código.
  • Puedes establecer sentencias condicionales para que solo activen los puntos de interrupción según una expresión de Kotlin.
  • Watches te permite agrupar variables de interés durante la depuración.

Más información