Блог Android-разработчицы
849 subscribers
13 photos
179 links
ex-VK, ex-MTS, 6+ лет опыта. Team Lead

Заметки, интересные задачи с работы (в рамках NDA) и просто мои мысли.

Блог: https://dzen.ru/android_junior

Личный канал: https://t.me/android_junior_notes

Мой аккаунт: https://t.me/bagena
Download Telegram
А точно каждый компонент должен быть зарегистрирован в манифесте?

Нет, не точно. Broadcast Receiver можно зарегистрировать двумя способами: статически в манифесте или динамически в коде.

Стоит заметить, что начиная с Android 8 большинство неявных бродкастов нельзя регистрировать в манифесте. Неявный бродкаст, это когда сообщение отправляется вообще всем приложениям, которые подходят, а не только нашему. Почему нельзя? В Android 8 были большие доработки по оптимизации. Представьте, что куча приложений подписаны на какое-то системное событие. Каждый бродкаст в этом случае потребляет ресурсы каждый раз при получении сообщения. В следствии этого активно тратится батарея, пользователь грустит и удаляет приложение.

Но все еще можем регистрировать в коде.

Список всех интентов, для которых неявный бродкаст можно в манифесте регистрировать — https://developer.android.com/guide/components/broadc...

Из тех, которые я помню:

ACTION_BOOT_COMPLETED — отправляется один раз при загрузке. Необходим для многих приложений, чтобы они все проинициализировались, запланировали что им надо и прочее.

ACTION_TIMEZONE_CHANGED и похожие — чтобы приложении могли свои настройки поправить (например, будильники).

ACTION_CONNECTION_STATE_CHANGED — чтобы приложения знали про соединение.

Еще есть всякие флаги, связанные со звонками, сообщениями, сетью и сменой/удаления пользователя.

Google рекомендует использовать JobSheduler вместо неявных бродкастов.

Какие вопросы можно задать из этого ответа?

— А какие еще ограничения есть?

— Что за JobSheduler и почему его?
👍1
Почему setContentView() надо обязательно указывать в onCreate() в активити? Почему не в onResume или в onViewCreated?

Тут надо вспомнить жизненный цикл активити. Ответ кроется в том, что onCreate у активити вызывается только один раз. Именно в этом методе мы делаем большинство инициализаций.

setContentView() — метод, который позволяет разместить пользовательский интерфейс на экране. И это достаточно тяжелая операция, особенно учитывая, что может быть очень сложная иерархия. Её нужно делать один раз. Если делать каждый раз в onResume, то, например, просто посмотрев уведомление в шторке, операция будет выполняться снова, что ухудшает производительность, приложение может подвиснуть, пользователь расстроится и напишет гневный отзыв.

Вопросы, которые можно задать?

— Какие параметры можно передать в setContentView?
— Какой жизненный цикл у активити?
— А есть еще какие-то методы, которые вызываются только один раз?
— Как работает onCreate?
— Что будет, если мы вызовем finish() в одном из колбэков жизненного цикла, а чуть дальше напишем что-то еще?

Выполнится весь метод и только потом произойдет завершение. Почему так?

Все методы жизненного цикла всегда обрабатываются в главном потоке в порядке очереди. И вот если мы вызовем finish() в одном из методов жизненного цикла, то в эту очередь добавится сообщение, но оно не будет выполнено, пока не вернется выполняемый callback, т.е. пока главный поток не освободится от текущей задачи.

— Когда в activity вызывается finish(), какие методы жизненного цикла вызываются?

onPause — onStop — onDestroy. Тут важно, что метод onSaveInstanceState не вызывается, потому что активити полностью уничтожается.
Какая разница между val и var?

Такое точно не спросят, но просто повторим для общего образования.

var — изменяемое значение. Переменная может изменить свое значение с одного на другое. Это аналогично тому как мы в Java объявляем переменные.

val — переменная, которая разрешает чтение, но запрещает запись. Из этого следует, что val должна быть проинициализирована сразу, потому что она не может меняться после создания.
Разница между const и val

Оба слова используются для объявления неизменяемой переменной. Так в чем же разница?

const используется для объявления тех свойств, которые являются неизменяемыми по своей природе, т.е. они должны определяться во время компилирования. Во время выполнения константу нельзя объявить и проинициализировать.

Для констант есть несколько требований:

— Должна быть на уровне top-level или членом object или companion object

— Это должна быть строка или примитивный тип;

— Без кастомных геттеров.

Из этого следует, что мы не можем сделать константой какую-то функцию или класс, потому что они инициализируются в рантайме.

val мы можем инициализировать в рантайме. Например, можно в коде написать что-то в стиле valCatName = getCatName() и оно будет работать.

Зачем использовать const, если можно всегда использовать val? Когда мы используем константу, то она создается во время компиляции и не будет никаких дополнительных расходов во время выполнения.
Когда не вызывается onPause и onStop?

Если в onCreate будет finish(). Аналогично, если finish() будет в onStart(), то вызовутся все методы, начиная с парного, т.е. с onStop.

Если finish() поставим в onResume(), то вызовутся все методы, начиная с onPause.

Может ли так быть, что не вызовется onDestroy?

Такое действительно может быть, поэтому не стоит использовать этот метод для сохранения данных. Бывают ситуации, когда система может убить процесс, которому принадлежит активити, не вызывая этот метод.

Ну и вспомнила еще один похожий вопрос. Может ли быть, что не вызовется finally?

Да, и таких вариантов на самом деле много:
— Вызываем System.exit(0) в блоке try;
— У JVM закончилась память;
— Наш джава-процесс убит;
— Дедлок в блоке try;
— Когда наше устройство выключается (например, из-за зарядки).
Сегодня разберем немного простых вопросов.

Что ты знаешь про lateinit?

Это один из самых спорных инструментов. Его или любят и везде используют или опасаются и яростно оберегают проект от подобного.

Довольно часто бывает, что мы хотим объявить переменную, но проинициализировать её позже. Конечно, можно сделать private var cat: String? = null, но что если переменная не должна быть null?

Тут нам поможет слово lateinit. Оно позволяет объявить переменную, но проинициализировать её в другом месте. И вот тут самый большой подвох. Может случиться так, что мы попробуем получить значение до того, как присвоим его переменной. И тогда мы получим UninitializedPropertyAccessException. Поэтому, если не уверены, то лучше предварительно проверять через this::cat.isInitialized (выражение вернет true или false), а потом уже пытаться получить значение.

Я отношусь к тем людям, которые предпочитают не использовать lateinit. Мы не роботы и обязательно случится так, что мы где-то попробуем получить значение раньше или что-то случится и в итоге приложение упадет с исключением.

Кстати, тут еще можно добавить, что lateinit как раз используется в даггере при инжекте объекта. Но тогда стоит ожидать, что собеседующий дальше спросит про даггер и перейдет к этой теме.
Как запустить компонент андроида в другом процессе?

Тут нам поможет android:process="имя_процесса" в манифесте.

Обычно все компоненты приложения выполняют в процессе, созданным для приложения. Но бывают ситуации, когда нужно какой-то компонент вынести в другой процесс. За три года моей работы я встречала такое только один раз. Но встречала же, так что вполне реальная ситуация.

Если имя указанного процесса начинается с ':', то создается новый процесс. Если двоеточия нет, то будет выполняться в существующем процессе с таким именем. Так разные компоненты в разных приложениях могут использовать один процесса, тем самым сокращая потребление ресурсов.

Новый процесс — достаточно тяжелое действие, так что лучше его не использовать, если это не необходимо.
👍1
Вспоминаем основное про манифест.

Обычно про него задают довольно поверхностные вопросы, так что кратко пройдемся. Впрочем, много про него и не рассказать.

Манифест — то, что должно быть в каждом приложении. Там описывается основная информация о нашем приложении для ОС Андроид, Google Play и инструментов сборки.

В манифесте обязательно должны быть указаны:

— Компоненты, которые мы используем, и их имена. Если не укажем, то система не сможет стартовать компонент. Интент-фильтры, если используем.

— Необходимые разрешения, в которых приложение нуждается. А еще разрешения, которые другие приложения должны иметь, чтобы получить доступ к данным нашего приложения. Начиная с 6 андроида пользователь может отклонять какие-то разрешения в рантайме. Но мы все равно должны указать их в манифесте.

— Различные требования к девайсам, которые необходимы для корректной работы приложения. Например, минимальная версия системы.

Что можно спросить?
— Что за интент-фильтры?
— А что там с разрешениями? Какие знаешь и какие нужны?
👍1
Очень кратко про рефлексию.

Рефлексия — это специальное API, которое позволяет получить доступ к данным приложения во время выполнения.

С помощью рефлексии мы можем узнать кучу всего о классах, методах и т.п. А еще мы можем получить приватные поля и даже изменить их значения. И можем создавать экземпляры класса прямо во время выполнения.

Рефлексия работает через java.lang.Class. JVM создает его для каждого объекта и именно через него мы можем всё делать.

Почему не любят рефлексию? Чуть выше я писала, что так мы можем получать доступ к приватным полям и менять значения. И тут мы напрочь ломаем инкапсуляцию и архитектуру приложения. Плюс дополнительно из-за того что мы все делаем динамически, снижается производительность. Ну и код, написанный с помощью рефлексии, довольно сложный и запутанный.