- Что как снять логи на Android?
- Способы снять логи для VK
- 1 способ. Через «Звонилку»
- 2 способ. Через Debug в меню
- Способы снять логи как для VK, так и для VK Me
- 1 способ. С помощью Android SDK Platform Tools
- 2 способ. С помощью ADB Logcat
- Android: логгирование и отправка результатов на почту
- Сбор и хранение логов.
- Передача файлов в другие приложения
- Отправка писем с логами.
- Логирование в Android приложениях
- Пытаемся навести порядок
- Снятие логов, скриншотов с мобильных девайсов
- Гайд по отладке Android-приложения: ищем баги и читаем логи
Что как снять логи на Android?
Лог-файлы — файлы, которые содержат в себе информацию о том, что происходит в системе, в них заносятся все действия пользователя и программы в хронологическом порядке.
Для чего нужны логи? Чаще всего логи нужны для того, чтобы понять причину падения приложений. Нередко случается так, что одно и то же падение может быть вызвано разными факторами и действиями. Есть несколько способов снять лог-файлы.
Способы снять логи для VK
1 способ. Через «Звонилку»
- Откройте «телефон».
- Введите *#*#856682583#*#* в телефоне и нажмите на вызов.
- В открывшемся меню нажмите «Логирование API» и «Запись логов в файл».
4. Откройте приложение, чтобы повторить все действия, приводящие к багу.
5. Закройте приложение, убрав его из недавно открытых.
После последнего действия логи автоматически загрузятся в ваши документы, далее не забудьте их прикрепить в отчёт.
Нередко встречаются случаи, когда 1 способ может не работать. С таким могут столкнуться пользователи Samsung, поэтому следующий способ для них.
2 способ. Через Debug в меню
- Откройте в браузере на ПК или телефоне по ссылке vk.com/apps?act=manage.
- Создайте любое приложение.
3. Перезапустите клиент.
4. Откройте настройки вашего профиля.
5. Нажмите на появившийся пункт «Debug».
6. Далее поставьте галку на «Логирование API».
7. Далее нажмите на «Запись логов в файл».
8. Откройте приложение, чтобы повторить все действия, приводящие к багу.
9. Закройте приложение, убрав его из недавно открытых.
10. Далее действуйте по той же инструкции.
При использовании 1 и 2 способов могут возникнуть ошибки с тем, что файл будет бесконечно загружаться в ваши документы. Чтобы это исправить, необходимо остановить процесс приложения VK через настройки телефона, переместить лог-файлы из папки /VK/logs в другую папку и только после этого запускать приложение. После запуска приложения, отправьте ваши лог-файлы в ваши документы из папки, в которую вы их переместили.
Способы снять логи как для VK, так и для VK Me
1 способ. С помощью Android SDK Platform Tools
Если вы разбираетесь в Android SDK Platform Tools, то вы можете воспользоваться следующей командой:
(adb shell am broadcast -a android.provider.Telephony.SECRET_CODE -d android_secret_code://856682583)
2 способ. С помощью ADB Logcat
Первое, что нужно сделать, — установить драйверы для утилиты adb:
- Для этого скачайте архив DumpAndroidApps_ru.zip
- Распакуйте архив, выгрузив все документы в нужную вам папку (как это сделать, читайте в этой статье).
- После чего запустите файл adb-setup-1.4.2.exe от имени администратора (ПКМ по файлу).
4. Запустите процесс установки, для этого нажмите Y и Enter при появлении каждого запроса в окне.
Источник
Android: логгирование и отправка результатов на почту
Хочу начать небольшой разговор о том, как можно получать данные о работе приложения и некоторых его компонентов от пользователей.
Одно дело — разработка, LogCat в Android Studio (если вы из любителей пожестче — можно распечатку в консоли смотреть с помощью adb), и совсем другое — ломать голову над вопросом почему у вас все работает на всем парке тестовых устройств, а пользователь жалуется на абсолютно обратную ситуацию. Коммуникация между разработчиком и конечным пользователем — это хорошо, но совсем другое — видеть своими глазами картинку происходящего (помните, как в матрице — для кого-то это зеленые иероглифы, а для кого-то — женщина в красном?)
Предлагаю разбить задачу на несколько частей, а именно — сбор и хранение логов, способ их передачи из одного приложения в другие с помощью FileProvider, ну и небольшой helper класс для создания писем с аттачами. Итак, поехали.
Сбор и хранение логов.
Кто-то использует System.out.println, кто-то — статические методы класса Log. Я с некоторых пор пришел к написанию своего класса для распечатки логов. Давайте вкратце расскажу почему.
Во-первых, это проще. Как правило, для отслеживания изменений в процессе выполнения приложения я использую одну и ту же метку. И вот однажды я подумал — зачем ты пишешь постоянно Log.i(MY_TAG, «info») если можно сократить немного и убрать из этой формулы одну постоянную?
Во-вторых, расширение логгирования. Это конкретно упирается в нашу задачу — хранение логов в файлах. Можно написать отдельный класс, в который будем передавать какие-то данные, как то: данные и имя файла, но данные мы уже передаем в метод Log.i / Log.e / проч., создавать лишний раз переменную что ли для этого? Некрасиво все это как-то.
Ладно, довольно лирики, давайте лучше взглянем на класс Diagnostics.
Для того, чтобы вывести информацию в LogCat с дефолтной меткой, достаточно написать следующее:
Иногда мне хочется видеть какие методы вызываются и в каких объектах. И с какими параметрами или значениями переменных. В общем, тут важно для меня — где именно производится вызов. Тогда я использую следующую конструкцию
Diagnostics.i(this, “onCreate w/param1 = “ + param1);
где this — это экземпляр класса Caller. Например, для MainActivity вы увидите следующее:
03–29 12:31:53.203 16072–16072/com.isidroid.platform I/Diagnostics: MainActivity.onCreate w/param1 = 200
И все сразу становится понятно — кто вызывает и где вызывает.
А теперь о хранении этой информации.
Как вы уже могли видеть, в классе Diagnostics есть методы для работы с файлами — createLog и appendLog. Объяснять, я думаю, не стоит — первый создает файл, второй — добавляет в него строку. Для новичков или тех, кто ленится читать код, уточню — appendLog создает файл, если его не существует, а createLog всегда создает новый. Чтобы лишней информации там не хранилось.
Файлы хранятся в cache директории, которая, к слову, недоступна для других приложений (ну, если у вас телефон не рутован, конечно).
В общем, теперь процедура распечатки лога и хранения его в файле выглядит следующим образом.
Надеюсь, это выглядит просто в использовании.
Передача файлов в другие приложения
Как я уже говорил выше, наши файлы для лога хранятся в некоторой защищенной от чужих глаз папке. Она настолько защищена, что если вы попробуете передать файлы в другое приложение с использованием относительного пути File.getAbsolutePath(), то вы потерпите неудачу.
На помощь к нам мчится FileProvider, друзья!
Вообще, в документации есть отличная статья (она же — пошаговая инструкция) на эту тему — Setting Up File Sharing, но для тех, кто предпочитает читать StackOverFlow и isidroid.com, я приведу выжимку из статьи с кодом реализации.
- Добавляем FileProvider в Manifest.
2. Указываем директории, доступные для шаринга. Для этого создаем файл res/xml/cache_file_paths и для нашего конкретного примера заполняем его.
Конец.
Нет, правда, это все.
На самом деле это довольно мощный инструмент для работы с файлами в вашем приложении, но в рамках поставленной задачи это все, что нам нужно сделать. Подробности — в официальной документации.
Отправка писем с логами.
Мы с вами почти добрались до конца, осталось дело за малым. Вообще, создание намерения (intent) для отправки писем — это довольно тривиальная задача, чтобы под нее писать отдельный хелпер. Но с другой стороны, если можно причесать код в вашей Activity / Fragment, то почему бы и нет, верно?
Гораздо симпатичнее будет выглядеть какой-нибудь строитель (builder) в коде нежели условия, проверки и лишние циклы. Я за то, чтоб это выносить в отдельный класс (кстати, не только я ратую за разделение представления от бизнес-логики).
Давайте перейдем сразу к сути. Сначала я покажу класс (который вы можете скопировать и использовать не глядя, конечно), а потом пример его использования. Поехали!
Где this — это Activity.
Вы можете самостоятельно указать «рыбу» для текста письма, но я рекомендую использовать те данные, которые указаны в методе buildContent, расширяя их при необходимости. Можно конечно извернуться и применить паттерн «декоратор» для расширения этих данных без модификации класса FeedbackHelper, но лично для меня необходимости в этом не было… Что до вас, то дерзайте!
Источник
Логирование в Android приложениях
Уверен, что всем разработчикам приложений на платформе Android знаком класс Log, позволяющий логировать различные события. По различным причинам, формат записи логов для каждого проекта может отличаться достаточно сильно — начиная от «AAA», «111111» и «I was here» до более-менее внятных — «Opening HTTP connection to habrahabr.ru». Под катом вы найдете пример функции, которая поможет навести порядок в логах.
Данный топик не претендует на оригинальность и универсальность. И поэтому, если в вашем проекте уже существует некий стандарт логирования событий, то смело проходите мимо — топик скорее ориентирован на начинающих разработчиков.
Как правило, ценность логов начинаешь понимать только когда заказчик матерясь отсылает лог на почту и просит засабмитить фикс через 5 минут. И если лог состоит из сообщений невнятного характера, то как минимум, разбр данного лога займет куда больше времени, чем хотелось бы.
Пытаемся навести порядок
Логи существуют для того, чтобы разработчик мог понять что, где и когда произошло. Найти ответ на вопрос «когда произошло» достаточно просто — в логах Андройд записывает время события. Нахождение ответа на вопрос «что произошло» так же не вызывает больших трудностей, если сообщение в лог было написано со смыслом, например: «Opening file. ». Вопрос «где произошло» оказывается наиболее сложным. Если проект большой, то придеться потратить время на нахождение нужного места кода, даже, если лог был написан со смыслом.
Если событие логируется с указанием Throwable (чаще Exception), например, метод public static int d (String tag, String msg, Throwable tr) , то в консоле сообщений будет выведен стек, который поможет быстро идентифицировать место логирования. Но использование данного метода без особой необходимости до безобразия перегрузит лог ненужной информацией.
Если же логируется просто текст, то при логировании можно явно указывать место вызова. Например:
Однако, писать такое каждый раз — дело утомительное и неблагодарное.
Ниже приведен пример класса Log , который делает это автоматически.
Использование класса очень простое:
Результатом логирования данным способом будут примерно следующие строки:
Примечание:
По понятным причинам, данный способ мало пригоден для приложений «пропущенных» через обфускатор.
В общем-то все.
Прошу прощения, если эта статья показалась слишком тривиальной для хабра.
Источник
Снятие логов, скриншотов с мобильных девайсов
Крэш-лог (Crash Log) – файл, в котором хранится вся информация по ошибке неработоспособности/экстренного завершения работы программы.
Лог-файл (журнал событий, Log) – это файлы, содержащие системную информацию работы сервера или компьютера, в которые вносятся определенные действия пользователя или программы.
Снятие логов в Android:
- Использовать ddms.bat (находится в папке tools — Android sdk).
- Catlog.
- Screens — Power + Громкость.
Снятие логов с Android устройств с помощью LogCat:
- Необходимо установить JDK и скачать Android SDK.
- Включение отладки по USB на устройстве (в “About device” тапать на номер билда до тех пор, пока не включится режим разработчика).
- Отметить чекбокс “USB debugging” в “Developer options”.
- Запустить файл “monitor.bat”, который находится в папке с инструментами (c:\adt\sdk\tools\monitor.bat).
- В открывшемся окне выбрать устройство, с которого будет производиться логирование.
- Выполнить действия, которые должны быть залогированы, выбрать нужный участок и сохранить лог в файл.
Снятие логов в iOS:
- iTunes.
- Xcode.
- QuickTime Player.
- Organizer — Devices
/Library/Logs/CrashReporter/MobileDevice.
Снятие логов посредством iTunes: нужно подключить устройство к компьютеру, запустить iTunes, выбрать Ваше устройство слева и нажать синхронизировать. В результате, все логи с устройства будут записаны в папку вида (Windows 7) — c:\Users\[ИмяПользователя]\AppData\Roaming\Apple Computer\Logs\CrashReporte.
Для просмотра краш-репортов нужно подключить iOS-устройство к компьютеру, нажать кнопку «Доверять» на мобильном устройстве. Запустить Xcode и перейти в Window → Devices and Simulators.
Источник
Гайд по отладке Android-приложения: ищем баги и читаем логи
Иногда в приложении встречаются ошибки, которые нельзя увидеть даже после запуска. Например, код компилируется, проект запускается, но результат далёк от желаемого: приложение падает или вдруг появляется какая-то ошибка (баг). В таких случаях приходится «запасаться логами», «брать в руки отладчик» и искать ошибки.
Часто процесс поиска и исправления бага состоит из трёх шагов:
- Воспроизведение ошибки — вы понимаете, какие действия нужно сделать в приложении, чтобы повторить ошибку.
- Поиск места ошибки — определяете класс и метод, в котором ошибка происходит.
- Исправление ошибки.
Если приложение не падает и чтение логов ничего не даёт, то найти точное место ошибки в коде помогает дебаггер (отладчик) — инструмент среды разработки.
Чтобы посмотреть на логи и воспользоваться дебаггером, давайте напишем простое тестовое (и заведомо неправильное) приложение, которое даст нам все возможности для поиска ошибок.
Это будет приложение, которое сравнивает два числа. Если числа равны, то будет выводиться результат «Равно», и наоборот. Начнём с простых шагов:
- Открываем Android Studio.
- Создаём проект с шаблоном Empty Activity.
- Выбираем язык Java, так как его, как правило, знают больше людей, чем Kotlin.
Нам автоматически откроются две вкладки: activity_main.xml и MainActivity.java. Сначала нарисуем макет: просто замените всё, что есть в activity_main.xml, на код ниже:
Можете запустить проект и посмотреть, что получилось:
Теперь оживим наше приложение. Скопируйте в MainActivity этот код:
В этом коде всё просто:
- Находим поля ввода, поле с текстом и кнопку.
- Вешаем на кнопку слушатель нажатий.
- По нажатию на кнопку получаем числа из полей ввода и сравниваем их.
- В зависимости от результата выводим «Равно» или «Не равно».
Запустим приложение и введём буквы вместо чисел:
Нажмём на кнопку, и приложение упадёт! Время читать логи. Открываем внизу слева вкладку «6: Logcat» и видим:
Читать логи просто: нужно найти красный текст и прочитать сообщение системы. В нашем случае это java.lang.NumberFormatException: For input string: «f». Указан тип ошибки NumberFormatException, который говорит, что возникла какая-то проблема с форматированием числа. И дополнение: For input string: «f». Введено “f”. Уже можно догадаться, что программа ждёт число, а мы передаём ей символ. Далее в красном тексте видно и ссылку на проблемную строку: at com.example.appdebugging.MainActivity$1.onClick(MainActivity.java:26). Проблема в методе onClick класса MainActivity, строка 24. Можно просто кликнуть по ссылке и перейти на указанную строку:
Конечно, метод parseInt может принимать только числовые значения, но никак не буквенные! Даже в его описании это сказано — и мы можем увидеть, какой тип ошибки этот метод выбрасывает (NumberFormatException).
Здесь мы привели один из примеров. Типов ошибок может быть огромное количество, все мы рассматривать не будем. Но все ошибки в Logcat’е указываются по похожему принципу:
- красный текст;
- тип ошибки — в нашем случае это NumberFormatException;
- пояснение — у нас это For input string: «f»;
- ссылка на строку, на которой произошла ошибка — здесь видим MainActivity.java:26.
Исправим эту ошибку и обезопасим себя от некорректного ввода. Добавим в наши поля ввода android:inputType=»number», а остальной код оставим без изменений:
Теперь можем вводить только числа. Проверим, как работает равенство: введём одинаковые числа в оба поля. Всё в порядке:
На равенство проверили. Введём разные числа:
Тоже равно. То есть приложение работает, ничего не падает, но результат не совсем тот, который требуется. Наверняка вы и без дебаггинга догадались, в чём ошибка, потому что приложение очень простое, всего несколько строк кода. Но такие же проблемы возникают в приложениях и на миллион строк. Поэтому пройдём по уже известным нам этапам дебаггинга:
- Воспроизведём ошибку: да, ошибка воспроизводится стабильно с любыми двумя разными числами.
- Подумаем, где может быть ошибка: наверняка там, где сравниваются числа. Туда и будем смотреть.
- Исправим ошибку: сначала найдём её с помощью дебаггера, а когда поймём, в чём проблема, — будем исправлять.
И здесь на помощь приходит отладчик. Для начала поставим точки останова сразу в трёх местах:
Чтобы поставить или снять точку останова, достаточно кликнуть левой кнопкой мыши справа от номера строки или поставить курсор на нужную строку, а затем нажать CTRL+F8. Почему мы хотим остановить программу именно там? Чтобы посмотреть, правильные ли числа сравниваются, а затем определить, в какую ветку в нашем ветвлении заходит программа дальше. Запускаем программу с помощью сочетания клавиш SHIFT+F9 или нажимаем на кнопку с жучком:
Появится дополнительное окно, в котором нужно выбрать ваш девайс и приложение:
Вы в режиме дебага. Обратите внимание на две вещи:
- Точки останова теперь помечены галочками. Это значит, что вы находитесь на экране, где стоят эти точки, и что дебаггер готов к работе.
- Открылось окно дебага внизу: вкладка «5: Debug». В нём будет отображаться необходимая вам информация.
Введём неравные числа и нажмём кнопку «РАВНО?». Программа остановилась на первой точке:
- Сразу подсвечивается синим строка, где программа остановлена: в окне кода на 28-й строке и в левом окне отладчика (там даже можно увидеть, какой метод вызван, — onClick).
- В правом, основном окне отладчика, всё гораздо интереснее. Здесь можно увидеть инстансы наших вью (answer, first, second), в конце которых серым текстом даже отображаются их id. Но интереснее всего посмотреть на firstInt и secondInt. Там записаны значения, которые мы сейчас будем сравнивать.
Как видим, значения именно такие, какие мы и ввели. Значит, проблема не в получении чисел из полей. Давайте двигаться дальше — нам нужно посмотреть, в правильную ли ветку мы заходим. Для этого можно нажать F8 (перейти на следующую строку выполнения кода). А если следующая точка останова далеко или в другом классе, можно нажать F9 — программа просто возобновит работу и остановится на следующей точке. В интерфейсе эти кнопки находятся здесь:
Остановить дебаггер, если он больше не нужен, можно через CTRL+F2 или кнопку «Стоп»:
В нашем случае неважно, какую кнопку нажимать (F9 или F8). Мы сразу переходим к следующей точке останова программы:
Ветка правильная, то есть логика программы верна, числа firstInt и secondInt не изменились. Зато мы сразу видим, что подпись некорректная! Вот в чём была ошибка. Исправим подпись и проверим программу ещё раз.
Мы уже починили два бага: падение приложения с помощью логов и некорректную логику (с помощью отладчика). Хеппи пас (happy path) пройден. То есть основная функциональность при корректных данных работает. Но нам надо проверить не только хеппи пас — пользователь может ввести что угодно. И программа может нормально работать в большинстве случаев, но вести себя странно в специфических состояниях. Давайте введём числа побольше и посмотрим, что будет:
Не сработало — программа хочет сказать, что 1000 не равна 1000, но это абсурд. Запускаем приложение в режиме отладки. Точка останова уже есть. Смотрим в отладчик:
Числа одинаковые, что могло пойти не так? Обращаем внимание на тип переменной — Integer. Так вот в чём проблема! Это не примитивный тип данных, а ссылочный. Ссылочные типы нельзя сравнивать через ==, потому что будут сравниваться ссылки объектов, а не они сами. Но для Integer в Java есть нюанс: Integer может кешироваться до 127, и если мы вводим по единице в оба поля числа до 127, то фактически сравниваем просто int. А если вводим больше, то получаем два разных объекта. Адреса у объектов не совпадают, а именно так Java сравнивает их.
Есть два решения проблемы:
- Изменить тип Integer на примитив int.
- Сравнивать как объекты.
Не рекомендуется менять тип этих полей в реальном приложении: числа могут приходить извне, и тип лучше оставлять прежним. Изменим то, как мы сравниваем числа:
Всё работает. Наконец-то! Хотя… Давайте посмотрим, что будет, если пользователь ничего не введёт, но нажмёт на кнопку? Приложение опять упало… Смотрим в логи:
Опять NumberFormatException, при этом строка пустая. Давайте поставим точку останова на 26-й строке и заглянем с помощью отладчика глубже.
Нажмём F8 — и перейдём в глубины операционной системы:
Интересно! Давайте обернём код в try/catch и посмотрим ошибке в лицо. Если что, поправим приложение. Выделяем код внутри метода onClick() и нажимаем Ctrl+Alt+T:
Выбираем try / catch, среда разработки сама допишет код. Поставим точку останова. Получим:
Запускаем приложение и ловим ошибку:
Действительно, как и в логах, — NumberFormatException. Метод parseInt выбрасывает исключение, если в него передать пустую строку. Как обрабатывать такую проблему — решать исключительно вам. Два самых простых способа:
- Проверять получаемые строки first.getText().toString() и second.getText().toString() на пустые значения. И если хоть одно значение пустое — говорить об этом пользователю и не вызывать метод parseInt.
- Или использовать уже готовую конструкцию try / catch:
Теперь-то точно всё в порядке! Хотя профессиональным тестировщикам это приложение никто не отдавал: поищете ещё ошибки? 🙂
Иногда в приложении встречаются ошибки, которые нельзя увидеть даже после запуска. Например, код компилируется, проект запускается, но результат далёк от желаемого: приложение падает или вдруг появляется какая-то ошибка (баг). В таких случаях приходится «запасаться логами», «брать в руки отладчик» и искать ошибки.
Часто процесс поиска и исправления бага состоит из трёх шагов:
- Воспроизведение ошибки — вы понимаете, какие действия нужно сделать в приложении, чтобы повторить ошибку.
- Поиск места ошибки — определяете класс и метод, в котором ошибка происходит.
- Исправление ошибки.
Если приложение не падает и чтение логов ничего не даёт, то найти точное место ошибки в коде помогает дебаггер (отладчик) — инструмент среды разработки.
Чтобы посмотреть на логи и воспользоваться дебаггером, давайте напишем простое тестовое (и заведомо неправильное) приложение, которое даст нам все возможности для поиска ошибок.
Это будет приложение, которое сравнивает два числа. Если числа равны, то будет выводиться результат «Равно», и наоборот. Начнём с простых шагов:
- Открываем Android Studio.
- Создаём проект с шаблоном Empty Activity.
- Выбираем язык Java, так как его, как правило, знают больше людей, чем Kotlin.
Нам автоматически откроются две вкладки: activity_main.xml и MainActivity.java. Сначала нарисуем макет: просто замените всё, что есть в activity_main.xml, на код ниже:
Можете запустить проект и посмотреть, что получилось:
Теперь оживим наше приложение. Скопируйте в MainActivity этот код:
В этом коде всё просто:
- Находим поля ввода, поле с текстом и кнопку.
- Вешаем на кнопку слушатель нажатий.
- По нажатию на кнопку получаем числа из полей ввода и сравниваем их.
- В зависимости от результата выводим «Равно» или «Не равно».
Запустим приложение и введём буквы вместо чисел:
Нажмём на кнопку, и приложение упадёт! Время читать логи. Открываем внизу слева вкладку «6: Logcat» и видим:
Читать логи просто: нужно найти красный текст и прочитать сообщение системы. В нашем случае это java.lang.NumberFormatException: For input string: «f». Указан тип ошибки NumberFormatException, который говорит, что возникла какая-то проблема с форматированием числа. И дополнение: For input string: «f». Введено “f”. Уже можно догадаться, что программа ждёт число, а мы передаём ей символ. Далее в красном тексте видно и ссылку на проблемную строку: at com.example.appdebugging.MainActivity$1.onClick(MainActivity.java:26). Проблема в методе onClick класса MainActivity, строка 24. Можно просто кликнуть по ссылке и перейти на указанную строку:
Конечно, метод parseInt может принимать только числовые значения, но никак не буквенные! Даже в его описании это сказано — и мы можем увидеть, какой тип ошибки этот метод выбрасывает (NumberFormatException).
Здесь мы привели один из примеров. Типов ошибок может быть огромное количество, все мы рассматривать не будем. Но все ошибки в Logcat’е указываются по похожему принципу:
- красный текст;
- тип ошибки — в нашем случае это NumberFormatException;
- пояснение — у нас это For input string: «f»;
- ссылка на строку, на которой произошла ошибка — здесь видим MainActivity.java:26.
Исправим эту ошибку и обезопасим себя от некорректного ввода. Добавим в наши поля ввода android:inputType=»number», а остальной код оставим без изменений:
Теперь можем вводить только числа. Проверим, как работает равенство: введём одинаковые числа в оба поля. Всё в порядке:
На равенство проверили. Введём разные числа:
Тоже равно. То есть приложение работает, ничего не падает, но результат не совсем тот, который требуется. Наверняка вы и без дебаггинга догадались, в чём ошибка, потому что приложение очень простое, всего несколько строк кода. Но такие же проблемы возникают в приложениях и на миллион строк. Поэтому пройдём по уже известным нам этапам дебаггинга:
- Воспроизведём ошибку: да, ошибка воспроизводится стабильно с любыми двумя разными числами.
- Подумаем, где может быть ошибка: наверняка там, где сравниваются числа. Туда и будем смотреть.
- Исправим ошибку: сначала найдём её с помощью дебаггера, а когда поймём, в чём проблема, — будем исправлять.
И здесь на помощь приходит отладчик. Для начала поставим точки останова сразу в трёх местах:
Чтобы поставить или снять точку останова, достаточно кликнуть левой кнопкой мыши справа от номера строки или поставить курсор на нужную строку, а затем нажать CTRL+F8. Почему мы хотим остановить программу именно там? Чтобы посмотреть, правильные ли числа сравниваются, а затем определить, в какую ветку в нашем ветвлении заходит программа дальше. Запускаем программу с помощью сочетания клавиш SHIFT+F9 или нажимаем на кнопку с жучком:
Появится дополнительное окно, в котором нужно выбрать ваш девайс и приложение:
Вы в режиме дебага. Обратите внимание на две вещи:
- Точки останова теперь помечены галочками. Это значит, что вы находитесь на экране, где стоят эти точки, и что дебаггер готов к работе.
- Открылось окно дебага внизу: вкладка «5: Debug». В нём будет отображаться необходимая вам информация.
Введём неравные числа и нажмём кнопку «РАВНО?». Программа остановилась на первой точке:
- Сразу подсвечивается синим строка, где программа остановлена: в окне кода на 28-й строке и в левом окне отладчика (там даже можно увидеть, какой метод вызван, — onClick).
- В правом, основном окне отладчика, всё гораздо интереснее. Здесь можно увидеть инстансы наших вью (answer, first, second), в конце которых серым текстом даже отображаются их id. Но интереснее всего посмотреть на firstInt и secondInt. Там записаны значения, которые мы сейчас будем сравнивать.
Как видим, значения именно такие, какие мы и ввели. Значит, проблема не в получении чисел из полей. Давайте двигаться дальше — нам нужно посмотреть, в правильную ли ветку мы заходим. Для этого можно нажать F8 (перейти на следующую строку выполнения кода). А если следующая точка останова далеко или в другом классе, можно нажать F9 — программа просто возобновит работу и остановится на следующей точке. В интерфейсе эти кнопки находятся здесь:
Остановить дебаггер, если он больше не нужен, можно через CTRL+F2 или кнопку «Стоп»:
В нашем случае неважно, какую кнопку нажимать (F9 или F8). Мы сразу переходим к следующей точке останова программы:
Ветка правильная, то есть логика программы верна, числа firstInt и secondInt не изменились. Зато мы сразу видим, что подпись некорректная! Вот в чём была ошибка. Исправим подпись и проверим программу ещё раз.
Мы уже починили два бага: падение приложения с помощью логов и некорректную логику (с помощью отладчика). Хеппи пас (happy path) пройден. То есть основная функциональность при корректных данных работает. Но нам надо проверить не только хеппи пас — пользователь может ввести что угодно. И программа может нормально работать в большинстве случаев, но вести себя странно в специфических состояниях. Давайте введём числа побольше и посмотрим, что будет:
Не сработало — программа хочет сказать, что 1000 не равна 1000, но это абсурд. Запускаем приложение в режиме отладки. Точка останова уже есть. Смотрим в отладчик:
Числа одинаковые, что могло пойти не так? Обращаем внимание на тип переменной — Integer. Так вот в чём проблема! Это не примитивный тип данных, а ссылочный. Ссылочные типы нельзя сравнивать через ==, потому что будут сравниваться ссылки объектов, а не они сами. Но для Integer в Java есть нюанс: Integer может кешироваться до 127, и если мы вводим по единице в оба поля числа до 127, то фактически сравниваем просто int. А если вводим больше, то получаем два разных объекта. Адреса у объектов не совпадают, а именно так Java сравнивает их.
Есть два решения проблемы:
- Изменить тип Integer на примитив int.
- Сравнивать как объекты.
Не рекомендуется менять тип этих полей в реальном приложении: числа могут приходить извне, и тип лучше оставлять прежним. Изменим то, как мы сравниваем числа:
Всё работает. Наконец-то! Хотя… Давайте посмотрим, что будет, если пользователь ничего не введёт, но нажмёт на кнопку? Приложение опять упало… Смотрим в логи:
Опять NumberFormatException, при этом строка пустая. Давайте поставим точку останова на 26-й строке и заглянем с помощью отладчика глубже.
Нажмём F8 — и перейдём в глубины операционной системы:
Интересно! Давайте обернём код в try/catch и посмотрим ошибке в лицо. Если что, поправим приложение. Выделяем код внутри метода onClick() и нажимаем Ctrl+Alt+T:
Выбираем try / catch, среда разработки сама допишет код. Поставим точку останова. Получим:
Запускаем приложение и ловим ошибку:
Действительно, как и в логах, — NumberFormatException. Метод parseInt выбрасывает исключение, если в него передать пустую строку. Как обрабатывать такую проблему — решать исключительно вам. Два самых простых способа:
- Проверять получаемые строки first.getText().toString() и second.getText().toString() на пустые значения. И если хоть одно значение пустое — говорить об этом пользователю и не вызывать метод parseInt.
- Или использовать уже готовую конструкцию try / catch:
Теперь-то точно всё в порядке! Хотя профессиональным тестировщикам это приложение никто не отдавал: поищете ещё ошибки? 🙂
Источник