Ускоряем дебаг в разы. Зачем и как использовать брейкпоинты
Всем привет! Меня зовут Вадим Джибалов, и я Android-разработчик в AGIMA. Свою первую статью на Хабре я посвящаю простой, но важной теме — брейкпоинты. Пишу я её для джуниоров, которые только знакомятся с отладкой. Мы уделяем много внимания развитию наших стажеров и росту специалистов. А когда только начинаешь программировать, найти даже элементарные вещи бывает нелегко. Мы готовы помочь.
Почему брейкпоинты — это круто
Когда я только начинал заниматься Android-разработкой, я не понимал истинную силу брейкпоинтов. Отладку я делал всегда через Timber и чувствовал себя прекрасно. Но однажды друг показал мне их истинную силу, и я понял, что зря их не ценил.
Сейчас я покажу эту силу и вам.
Но сначала разберемся, какие преимущества есть у брейкпоинтов по сравнению с отладкой через Timber, Log и подобные вещи:
Нет необходимости писать код и импортировать новые библиотеки там, где это не нужно.
Не нужно открывать консоль, чтобы проверить значение переменной или что-то ещё.
Во время «заморозки» брейкпоинта мы можем прочитать значение абсолютно любой переменной, доступной в нашем потоке.
Поставить брейкпоинт — это очень быстро.
Брейкпоинты можно ставить в рантайме — об этом расскажу ниже.
И это далеко не всё.
Чтобы понять, насколько это удобно, разберем пример. Сначала простой, где брейкпоинты не особо нужны и можно воспользоваться Timber, а потом кейс поинтереснее.
Допустим, у нас есть сценарий получения числа с бэка, проведения с ним каких-то операций и отображения на экране. Для симуляции получения числа используем простенькую функцию. Что она возвращает, мы, по идее, не знаем.
Далее нам это число нужно умножить на 2. Для этого мы тоже пишем отдельную функцию, которую в дальнейшем будем отлаживать. Ошибка допущена специально.
Выводим результат на экран и видим, что мы получили 220 вместо 20.
Давайте посмотрим, как бы примерно выглядела отладка без брейкпоинтов.
После этого мы бы открыли консоль и увидели неверный результат на втором логе.
Как выглядела бы отладка через брейкпоинты в данном случае?
Просто нажимаем один раз рядом с номером строки, и у нас выставляется брейкпоинт. Брейкпоинта нет на поле number, потому что на 15-й строке мы и так сможем прочесть её значение.
Запускаем программу и видим, что number у нас показывается, а formattedNumber неизвестен.
Вычислить любое выражение нам поможет Evaluate Expression. Нажимаем Alt + F8, и у нас выскакивает окно, в котором и происходит вся магия. Вписываем туда значение multipleBy2(number) и видим результат:
Мы поняли, что ошибка в функции multipleBy2, и закончили отладку.
Теперь разберём пример с большим количеством операций. Допустим, во время отладки мы зашли на экран и видим, что из-за неверных вычислений поехала вся вёрстка. Создадим функции a, b, c, d, e, которые будут вызываться друг за другом. Их общий результат будет влиять на состояние экрана. Они будут условно отвечать за разные компоненты на макете, и нам нужно выяснить следующее:
Какой метод ломает верстку.
На каком этапе это происходит (на какой строке).
Снова разберём 2 варианта отладки: с брейкпоинтами и без.
Если не использовать брейкпоинты, процесс будет примерно таким:
Удалять все методы из кода по порядку, пока не наткнемся на нужный > Вывести в консоль все данные из метода по порядку.
Этот вариант плох по нескольким причинам. Во-первых, нам нужно будет каждый раз пересобирать приложение. Большие проекты могут собираться более 10 минут, и это съедает невероятно много времени. Во-вторых, нужно будет писать код, импортировать библиотеки и т. д.
С брейкпоинтами наша жизнь становится намного проще. Как только мы видим ошибку в программе, ставим брейкпоинт на итог вычислений.
Возможности брейкпоинтов
Представим, что у нас есть viewModel и кнопка, по нажатию на которую отправляется количество нажатий на кнопку. Вся эта картина будет выглядеть примерно так:
Но вдруг мы обнаруживаем, что мы посылаем неверное значение. Нам на помощь придут брейкпоинты. В этом случае есть 2 способа достучаться до истины:
Поставить брейкпоинт внутри функции calculateData.
Поставить field WatchPoint на поле count.
С помощью брейкпоинтов мы можем посмотреть, откуда вызывался исполняемый код. Для этого нужно просто посмотреть в левую сторону окна дебага.
Давайте поставим брейкпоинт внутрь calculateData и посмотрим. что выйдет:
Заходим в приложение и видим, что функция calculateData вызывается моментально из непонятного места. Двойной клик по второй строчке (run:41, MainActivity…) укажет нам на фрагмент кода, который вызывает функцию calculateData.
Тут мы сразу можем увидеть, что какой-то разработчик захотел испортить нам жизнь и написал код, который ломает нашу логику.
Что такое Kotlin Field Watchpoint
Но что, если наша переменная менялась за пределами функции calculateData?
На помощь придёт field WatchPoint. Около переменной count попробуйте поставить брейкпоинт, и перед вами встанет выбор, что вам нужно:
Kotlin Field Watchpoint — это тот же самый брейкпоинт, но с одним отличием. Он будет останавливать работу кода при изменении переменной, а не при ее инициализации. Давайте проверим его работу.
Удалим метод calculateData в onStart и заменим его на count += 1
Теперь при запуске приложения через то же самое окно мы можем увидеть, откуда изменялась переменная.
Что об этом почитать:
Не стесняйтесь задавать вопросы в комментариях — на что-то отвечу здесь, о чем-то напишем отдельную статью. А еще подписывайтесь на наш телеграм-канал для разработчиков — там мои коллеги делятся полезными материалами для любого уровня.