Как для мобильных разработчиков менялся Android с 2014 года и до сегодняшнего дня? Казалось бы, можно просто открыть официальное описание каждой версии и узнать. Но интереснее не просто читать сухие чейнджлоги, а вместе вспомнить все и из отдельных фактов сделать общие выводы.
В июне я выступил с докладом об этом на конференции Mobius, а теперь для аудитории Хабра публикую его текстовую версию. Если есть что добавить — пишите в комментариях!
Если вам интересно следить за самыми последними новостями Android разработки и получать подборку интересных статей по этой тематике, тогда вам стоит подписаться на Telegram канала "Android Broadcast"!
Android 5.0 Lollipop
В моем понимании это было самое массивное изменение в Android ОС, очень важная веха.
ART VM. В первую очередь, появилась новая виртуальная машина для выполнения байт-кода, заменившая собой Dalvik, — Android Runtime. Она привнесла много важного:
-
поддержку процессоров x64;
-
Ahead-of-Time (AoT) компиляцию;
-
улучшения в Garbage Collector (сборщике мусора, далее GC) — работу в фоне, оптимизацию проходов GC, поддержку compactive GC.
Project Volta. Улучшение времени работы устройств от аккумулятора с помощью софта. Тогда в Google поняли, что есть две проблемы: сторонние разработчики приложений и вендоры. И с вендорами нужно дружить, а вот разработчики — это такие «пираты», которых нужно ограничивать.
JobScheduler. Новый API, позволяющий запускать фоновые задачи централизованным образом. Вроде бы хорошо для разработчиков, но для Android это способ контролировать все, что делает приложение, и разрешать или запрещать это.
WorkManager. Возможно, вам знаком не JobScheduler, а WorkManager, который портирует всю функциональность системного API на старые версии Android и расширяет возможности JobScheduler. Например, WorkManager позволяет выстраивать зависимости между задачами. А еще не использует JobScheduler на версиях Android до 6.0 и использует там AlarmManager, чтобы предоставить одинаковый опыт на всех поддерживаемых версиях Android. Сегодня WorkManager — это основной способ выполнения задач в фоне.
Battery Saver. До этого разные производители смартфонов уже пытались сэкономить заряд аккумулятора, но теперь Battery Saver впервые появился для системы в целом. Он включал:
-
уменьшение частоты процессора;
-
уменьшение частоты обновления дисплея;
-
ограничение потребления данных в фоне;
-
другие оптимизации от вендора.
Теперь через SDK разработчики получили возможность определять, запущен ли этот режим, и как-то на это реагировать.
Material Design. Первая попытка стандартизации дизайна приложений со своей идеологией: как и почему должна строиться дизайн-система, какие есть основные блоки.
Это очень продвинуло Android-разработку в отношении UI — теперь можно было говорить: «Вот у нас есть свой дизайн, давайте не будем делать просто как в iOS».
Render Thread. Специальный поток, созданный для разгрузки главного потока. Например, в RecyclerView есть возможность подготовить элементы списка до того, как они понадобятся пользователю. Это работа по максимуму выносится в Render Thread. Также туда ушли анимации и другие UI-операции.
ListView 2.0. Он не был представлен под таким названием и больше известен как RecyclerView. Тут проявилась еще одна важная тенденция: новые компоненты были уже не частью SDK, а частью библиотек Jetpack — он же AndroidX. Появилась возможность обновлять их независимо и не создавать фрагментацию в возможностях для разработчиков.
Поддержка векторной графики. Она позволила прорисовывать многое качественнее и с меньшим объемом файлов. Важно понимать, что поддержка векторной графики еще не означает полную поддержку SVG. Формат SVG содержит много возможностей для показа элементов, и Android поддерживает лишь их малую часть. Кроме того, векторная графика — это не полная замена растровой. Растр остался нужен для сложных изображений, потому что векторная графика предназначена для простых иконок.
Camera 2 API. Она сильно способствовала развитию камер. Но Camera 2 API требовала поддержки со стороны производителя телефона и была доступна не на всех устройствах. Не знаю, почему в Google так поступили, ведь еще с Project Volta они поняли, что вендоры — это тоже зло.
В итоге создали Jetpack-библиотеку CameraX для упрощения работы с камерой. Стандартизировали подходы вплоть до устранения разницы в режимах запуска камеры у смартфонов разных моделей. Например, запуск камеры у LG был строго в фоновом потоке, а у Samsung — в главном, хотя в спецификации API это не описывалось. Добавили поддержку вендорных расширений — ночного режима, HDR, портретных фото и других, а также стали упрощать работу в стандартных сценариях и интеграциях с популярными библиотеками. Например, для анализа содержимого в кадре.
Chrome WebView. Движок веб-страниц всегда был вшит в Android SDK, и обновление происходило вместе с прошивкой. Но в Android 5.0 его выделили в отдельное приложение в системе, которое обновляется независимо от прошивки. WebView, который вставляется в приложение, просто подключается к нему. Получается удаленный рендеринг в рамках устройства.
Android 6.0 Marshmallow
Runtime permissions. Наверное, Android-разработчики помнят эту версию Android в первую очередь именно благодаря этому пункту. Разрешения стали выдаваться приложению по одобрению пользователя, а не при установке. Особенно этому радовались специалисты по безопасности, потому что отсутствие запроса разрешений у пользователя представляло серьезную уязвимость для Android.
Doze Mode и App Standby. Получили продолжение идеи для снижения расхода батареи. Doze Mode — режим, который активируется, когда вы не пользуетесь телефоном, и оставляет работать только важные приложения. App Standby работает, когда вы не используете приложение и оно свернуто. Оно ограничивает доступ в сеть и прочие функции, чтобы приложение потребляло меньше энергии. Изначально Doze Mode включался примерно через полчаса после того, как телефон оказался неподвижным, фактически — когда вы ложились спать. App Standby включался сразу, как только вы сворачивали приложение или начинали пользоваться другими.
Firebase High Priority Cloud Messaging. Раз появились такие режимы сна, пришлось сделать изменения в Cloud Messaging. Возникло понятие High Priority Message — пуш, который способен разбудить устройство в режимах Doze Mode и App Standby.
Конечно, есть соблазн назначить высокий приоритет всем уведомлениям. Но нет — это не сработает. Нужно четко понимать приоритеты. Например, если у вас приложение с чатами и пользователь включил там уведомления, то нужен пуш с высоким приоритетом. А если это чат, в котором он отключил уведомления, то к чему там высокий приоритет?
Сканер отпечатка пальца. Частично появилась биометрия как часть API, а именно поддержка сканера отпечатка пальца. До этого биометрия уже была у отдельных вендоров, например у Samsung, в их реализациях операционной системы. Теперь это стало системным API, которое позволило взаимодействовать с ним из любого приложения и обеспечить защиту данных с быстрой разблокировкой доступа к ним со стороны пользователя.
Android 7.0 Nougat
Project Svelte. Появилась инициатива по сокращению расхода оперативной памяти. Кроме того, разработчики поняли, что приложения многовато работают в фоне.
Первый удар нанесли по системным бродкастам, которые уведомляют о событиях, например при изменении сети — CONNECTIVITY_ACTION.
Изменение сети в понимании Android — это не только переход с Wi-Fi на мобильную сеть, но и переход с 3G на 2G с изменением качества сигнала. И если при этом приложение не запущено, система поднимает его, чтобы доставить этот бродкаст. Соответственно, потом нужно остановиться или что-то сделать.
Представьте, что вы идете по улице. Качество связи не лучшее, чтобы везде стабильно было 4G или хотя бы 3G. У вас примерно 10% приложений из ста, которые на это реагируют. Сколько энергии нужно системе, чтобы обеспечивать доставку всех событий и чтобы при этом все работало?
Запустить процесс — недешевая операция. А если телефон еще и в спящем режиме, будут и другие накладки. Кажется, делать так не стоит.
Аналогичным образом, когда пользователь снимал фото или видео, это добавлялось в галерею и рассылался системный броадкаст (ACTION_NEW_PICTURE или ACTION_NEW_VIDEO). Решили, что этого тоже делать не нужно. На замену пришел JobScheduler, который должен запускать задачи на основе запрещенных бродкастов.
App Shortcut. Фича, которая появилась и в iOS, и в Android примерно в одно и то же время.
Смысл в том, что можно привязывать быстрые действия при нажатии на иконку приложения в лаунчере. В первой реализации их было максимум три, потом лимиты менялись. Их количество зависело и от вендоров.
Нужно понимать, что шорткаты развивались и дальше. Сейчас они доступны через поиск приложений. В некоторых лаунчерах они вставляются вверх как панель быстрого действия, то есть действие доступно не только через лаунчер.
ART Profiles. За несколько лет использования ART в проде разработчики Android ОС осознали недостатки AOT-компилятора:
-
долгая установка приложений;
-
необходимость хранить скомпилированный код на диске (требуется дополнительное место);
-
каждое обновление телефона требует повторной компиляции всех приложений;
-
постоянная трата заряда аккумулятора.
Преимущества у AOT остались. Поэтому сделали ART Profiles — гибрид AOT и JIT. Суть в том, что по умолчанию работает JIT-компилятор, но он также создает профили использования приложения. И на основе этого те куски кода, которыми особенно часто пользуется юзер, с помощью AOT будут скомпилированы заранее для быстрого запуска. Лучшее из двух миров!
Google Play Cloud Profiles. ART Profiles стали развивать, и появились Cloud Profiles. Суть была в том, чтобы помочь новым пользователям собирать профили с локальных устройств и передавать их в Google Play, который их объединяет, а при установке приложения сразу выдает профиль, позволяющий уменьшить время холодного старта, потому что часть приложения уже скомпилирована другими пользователями.
В первых реализациях система приводила к багам. Часто бывало, что мы брали одну и ту же сборку, заливали ее в Google Play, скачивали APK из Google Play, и все крэшилось. А вот APK, установленный сразу на телефон, не крэшился, потому что не доставлялся профиль. Сейчас так не происходит, но в первых реализациях это работало странно. С профилями нужно было учесть много переменных. Для переноса профиля с одного устройства на другое нужно, чтобы это устройство было абсолютно идентичным: та же версия Android, ART VM и другие переменные, которые могут на это повлиять.
А потом подумали: почему разработчик не может заранее создать профиль и положить его в приложение? Не так давно появились Baseline Profiles и стали частью Jetpack. Можно запустить специальный тест, сгенерировать профиль и положить его в приложение. Когда его отправят в Google Play, он возьмет Cloud Profiles, которые собрал от пользователей, ваш профиль, смерджит это и получит идеальный результат. Профиль будет оптимизирован и с вашей стороны, и со стороны других профилей, а конечный пользователь получит максимум.
Это позволяет работать как с частями, которые активны, так и с менее важными. Без Baseline Profiles в Jetpack Compose было бы очень плохо, ведь эти профили позволяют оптимизировать как библиотеки, так и приложения. С любой библиотекой можно собрать себе профиль и положить ее туда. А приложение, которое будет подключено, потом смерджится.
Если есть проблемы с холодным стартом или же вы очень волнуетесь на этот счет, попробуйте это — действительно должно помочь.
Vulkan API. Также прокачали OpenGL и добавили поддержку Vulkan API. Для геймеров это можно грубо объяснить как DirectX 12 из мира OpenGL. Это низкоуровневый API, который позволил убрать кучу слоев и быстрее обращаться с GPU.
Direct Boot. Еще одна важная фича. Идея в том, что, когда мы запускаем устройство, наше хранилище зашифровано и мы ничего не сможем сделать с устройством. В нем может работать только системный раздел.
Но что, если мы запустили устройство и забыли его разблокировать, но хотим получать SMS, звонки и прочее? Благодаря Direct Boot это возможно. Он позволяет работать с приложением до разблокировки файлового хранилища с помощью пароля. Вы можете работать в выделенном для этого спецразделе. Можно перенести данные. Если приложение критично для работы в такие моменты, обратите внимание на эту фичу.
A/B-обновления (бесшовные обновления). Почему Direct Boot стал так важен? В Android появились A/B-апдейты, или бесшовные апдейты.
Раньше Android обновлялся так: вендор собирает новую прошивку и присылает ее на устройство. Оно перезагружается, прошивка распаковывается в специальном режиме, накатывается на системный раздел, с которого загружается устройство, и происходит загрузка.
Вроде бы все хорошо. Но вот в чем проблема: устройство долго было выключено. Пока обновление распакуется — особенно если оно большое, — пройдет много времени. Второй момент: если вендор накосячил в прошивке, получится кирпич. Фактически испорчен системный раздел, и с ним больше нельзя работать.
A/B-обновления сводятся к тому, что появляются два раздела. Первый существует в системе, с которой вы работаете. Когда приходит новая прошивка, создается новый раздел, куда распаковывается новая системная часть. При следующей загрузке устройства произойдет переключение разделов. Все загрузится снова.
Тут есть большой плюс. Во-первых, если прошла кривая прошивка и загрузиться не получается, можно легко откатиться к старой. Во-вторых, не приходится ждать обновлений слишком долго. Вся распаковка прошивки происходит во время работы устройства, и с ним можно взаимодействовать.
Неприятности снова кроются в вендорах. Хоть фича вышла в Android 7.0 в 2016 году, Samsung все еще не поддерживает ее в 2022 году. Фича есть, но на практике с ее адаптацией производителями устройств не все так гладко.
Android 8.0 Oreo
В этой версии получило продолжение то, что было сделано ранее, и началась новая война — запрет фоновых Service.
Нет бродкастам. Раньше было заблочено только три системных бродкаста. Теперь стали блочить практически все, и проще назвать незаблоченные.
Исключения из ограничений:
-
ACTION_BOOT_COMPLETED;
-
ACTION_LOCALE_CHANGED;
-
ACTION_PACKAGE_DATA_CLEARED, ACTION_PACKAGE_FULLY_REMOVED;
-
ACTION_NEW_OUTGOING_CALL;
-
ACTION_MEDIA_;
-
SMS_RECEIVED_ACTION, WAP_PUSH_RECEIVED_ACTION.
Полный список исключений можно найти на специальной странице.
Эти ограничения действительны на лето 2022 года. Возможно, потом исключений станет еще меньше: им могут придумать альтернативу или просто решить, что они не нужны. Идея правильная, потому что часто бродкасты использовались для механизмов отлова, трекинга, слежения и прочего.
Нет фоновым Service. В Android 8.0 началась борьба с фоновыми Services (обычный запуск Service).
Первое ограничение: приложение в фоне не может запустить сервис.
Второе: если у вас есть активный сервис и приложение сворачивается, через пару секунд он будет убит. Временной промежуток может отличаться в зависимости от вендора, приоритетов и версии ОС — это могут быть как две секунды, так и пять. Но нельзя гарантировать, что сервис будет долго жить в фоновом режиме.
Foreground Service. Фоновые сервисы убиты, поэтому нужно активнее использовать Foreground Service. Теперь, чтобы явно сказать, что запускается Foreground Service, нужен был дополнительный метод, потому что сервисы Foreground и Background запускались одинаково.
Как выглядел запуск Foreground Service до Android 8.0:
context.startService(Intent(context, MediaService::class))
class MediaService : Service() {
override fun onCreate() {
super.onCreate()
startForeground(NOTIFICATION_ID, newOngoingNotification())
}
fun newOngoingNotification() : Notification
companion object {
const val NOTIFICATION_ID
}
}
Теперь сказали, что метод startService() не подходит. Нужно явно сказать, что запускается Foreground Service. Но есть тонкий момент. Между вызовами методов Context.startForegroundService() и Service.startForeground() есть ровно пять секунд. Если вы не успеете, произойдет крэш приложения. Ограничение не зависит от устройства — оно может быть как суперфлагманом, так и бюджетным.
context.startForegroundService(Intent(context, MediaService::class))
class MediaService : Service() {
override fun onCreate() {
super.onCreate()
// Метод должен быть вызван не больше чем через 5 секунд после вызова Context.startForegroundService()
startForeground(NOTIFICATION_ID, newOngoingNotification())
}
fun newOngoingNotification() : Notification
companion object {
const val NOTIFICATION_ID
}
}
Тут важный момент: как много инициализации делать в Application onCreate()? И это первая проблема. Обычно разработчики поступают по принципу «запихнем все в Application.onCreate()». Инициализация, аналитика, запуск соединения с сервером — зачастую эти операции стартуют при холодном запуске приложения. Фишка в том, что не все они нужны для сервиса. Например, они нужны для UI.
Для Android 8.0 очень важным стал вопрос оптимизации: надо явно указывать, чего вы хотите, понимать это и оптимизировать старт. Вот банальные советы:
-
делаем только необходимую инициализацию;
-
делаем ленивую инициализацию — по необходимости, а не заранее;
-
переносим инициализацию из главного потока (UI-потока) на фоновые.
Все их знают, но почему-то мало кто им следует. Не надейтесь на железо. Сегодня оно классное, а через два года уже неактуальная модель с производительностью на уровне середняков.
Project Treble. Следующая важная веха, которая сильно изменила фрагментацию в Android.
Почему, например, во времена Android 7.0 считалось феноменальным, если обновление телефона выходило через полгода? Проблема в том, что часть вендора и часть фреймворка были буквально вшиты друг в друга. Фактически это был монолит.
Чтобы обновить прошивку, вендору нужно было сделать следующее:
-
Дождаться драйверов от производителей железа, чипов и всего остального, что обновится под новую версию Android.
-
Забрать это все себе и перепиливать свой софт с учетом изменений.
-
Интегрировать все в монолит.
Google решил: а зачем вендорам вообще лезть в нашу часть, если можно ее вынести и абстрагировать через специальный интерфейс, который не будет зависеть от платформы? Фактически мы можем обновить фреймворк независимо от вендора.
Идея Project Treble в этом и состоит: отделить то, что делают производители устройств, от того, что делает Treble. Теперь можно обновить устройство независимо от вендора, если такая возможность есть в загрузчике.
Вы наверняка слышали о Generic image — штуке, которая появилась вместе с Android. Вы можете накатить бета-версию Android 12 или 13 на многие устройства, которые полноценно поддерживают Project Treble и могут работать у вас дальше. Благодаря этой штуке можно даже получить чисто Android.
Целью было решение проблемы фрагментации. Вроде бы поначалу все не взлетело, но вот схема от июня 2022 года, которую я использовал при подготовке доклада:
Как видите, популярность версий изменилась. Начиная с Android 8.0 практически 9 из 10 устройств работают на Android 8.0 и выше по статистике устройств, активных в Google Play. Раньше 25—30% уже считались хорошим показателем.
За 2020—2022 годы Google добился роста доли новых версий Android более чем на 10%. Сейчас ситуация становится лучше — Project Treble дал свои плоды.
Neural Networks API. Появилась стандартизация работы с нейронными сетями. Проблема заключалась в том, что работу с машинным обучением нужно аппаратно ускорять. Для этого у разных производителей есть свои чипы. Возможно, у кого-то есть встроенные в их CPU от Qualcomm, Mediatek или кого-то еще, но некоторые ставили свои. Яркий пример — чип Pixel Visual Core в смартфонах Google Pixel, который позволял аппаратно ускорять обработку фотографий.
В Google решили, что нужно создать прослойку и общий интерфейс для тех, кто разрабатывает API на C и работает с нейронными сетями. Они могут быть уверены, что код всегда будет выполняться максимально быстро.
Вендор внедрит реализацию аппаратного ускорения на соответствующем чипе, причем не важно, на каком именно. В крайнем случае API будет «фолбэчиться» на процессор, как это было и раньше.
Проблема опять заключалась в вендорах. Они выбрали стандартный CPU fallback, который не нужно было внедрять, потому что он был зашит в Android. Со временем стало лучше, но вендоры и тут оказались болью.
Каналы в уведомлениях. Раньше пользователь мог только включить или выключить все уведомления, но теперь появилась возможность управлять различными группами уведомлений и даже группами групп уведомлений через системные настройки. Разработчики смогли доставлять больше нужных событий без лишнего шума.
Картинка в картинке. А вот штука, за которую я больше всего люблю Android-смартфоны: в них можно смотреть все в фоновом режиме. Идея не нова, но раньше так могли только телевизоры, а теперь смогли и смартфоны.
Это довольно классная функция, если у вас есть видео или материал, который нужно показывать. Или что-то еще, что требует работы с несколькими окнами.
Загружаемые шрифты. Прокачали работу со шрифтами. Возможно, вы помните, что шрифты всегда клали в ассеты, и это был непонятный дикий костыль. В Android 8.0 решили наконец-то сделать шрифты полноценными ресурсами. Теперь их кладут в resources, и их можно описывать семейством и прочим. Но появилась важная фича. Зачем каждый разработчик кладет себе одни и те же шрифты? Ведь можно централизованно их скачивать. Они будут собраны вместе и уже готовы на устройстве.
Идея звучала классно. Проблема в том, что хоть Google и заявил, что есть много разных поставщиков шрифтов, на деле он только один — Google Fonts. Как всегда, хорошая идея на чем-то закончилась.
Concurrent Heap Compaction Collector. Также в Android 8.0 сильно обновили сборщик мусора. Изменились до неузнаваемости механизмы его работы и очистки: теперь она может происходить во время работы приложения и пользователь этого даже не заметит. Это стало возможно благодаря тому, что весь механизм работы с памятью разбили на кластеры, и сборщик мусора может их блокировать по отдельности. Подобным образом работа происходит с ConcurrentHashMap. Блокируются отдельные части, и, когда пользователю нужна память, она аллоцируется из других частей.
Kotlin + Android. Появилась официальная поддержка Kotlin, а через два года он стал основным языком. Это событие стало самым важным для Android-разработки после релиза Android Studio.
Android 9.0 Pie
Material Design 2.0. В Android 9.0 переработали Material Design и представили ее вторую версию. Закругленные края элементов, новый шрифт Google Sans, больше прозрачности, врезание элементов UI друг в друга и другие незначительные изменения.
App Standby Buckets. Прокачанный механизм App Standby из Android 6.0. Раньше Standby работал так: «Приложение свернули — я его сейчас заглушу». Сейчас Android смотрит, как используют приложение, категоризирует его в зависимости от использования и закидывает в группу приложений, опираясь на то, как его будут использовать.
На основе этого начинали выдаваться ограничения.
Есть две специальные категории: ACTIVE и NEVER. ACTIVE выдается только приложению, с которым сейчас работает пользователь. NEVER выдается всем приложениям, которые установили, но ни разу не запускали.
В зависимости от того, как часто использовались остальные приложения, накладывались разные ограничения. Например, если приложение попадает в категорию RARE и вы посылаете ему уведомление с высоким приоритетом, оно может прийти не чаще чем раз в сутки.
Разработчики любят бороться с системой и обходить ограничения. Если приложение попало в категорию RARE и вам это не нравится, скорее всего, пользователю ваше приложение не нужно. Поэтому вместо борьбы с системой разработчику стоит подумать, что он мог сделать не так и почему пользователь не запускает приложение. Система старается подстраиваться под пользователя.
Важно, что ограничения могут меняться в зависимости от ОС. Бакеты дорабатываются, меняются способы попадания туда приложения и так далее. Схема выше актуальна именно для Android 9.0. Не факт, что ограничения на различные функции не стали строже в более современных версиях Android или прошивке от вендора.
Разрешение на запуск Foreground Service. Продолжили усложнять жизнь разработчикам при работе с Foreground Service. Теперь, чтобы запускать их, вы должны были явно объявить разрешение FOREGROUND_SERVICE. Оно не требовало запроса и выдавалось автоматом, аналогично разрешению на доступ в интернет — типа «вы будете использовать Foreground Service».
non-SDK interfaces. Убрали лазейку для тех, кто любит вызывать скрытый API Android. Android SDK — набор публичного API. Но это не весь API, который можно запустить. Есть механизм рефлексии, который позволяет запустить в ОС что угодно из доступного вам. Всегда была приватная часть Android SDK, которую использовали, например, Google Service.
Те, кто хотел сделать хорошую запись звонков или собрать какую-то хитрую статистику, вызывали часть API через рефлексию. Google это не нравилось, поэтому стали делать non-SDK interfaces. Это значит, что, если API не публичный, даже вызов через рефлексию приведет к сбою. Теперь можно вызывать только для спецпроцессов.
В каждой версии Android эти non-SDK interfaces публикуются в первой Dev Preview, и вы можете с ними ознакомиться. Публикуются два списка: черный и серый. В черный входит все, что будет недоступно уже в этой версии Android, а в серой — то, что запретят использовать в следующей. Если что-то влияет на вашу работу, обязательно сообщайте Google, что у вас нет альтернативы. Кто знает, вдруг получится доказать, что вам это нужно?
Biometric API. Менеджер отпечатков пальцев унифицировали и сделали Biometric API. Если раньше для авторизации можно было использовать только палец, то теперь — какую угодно часть тела, если это поддерживает устройство. Например, лицо или сетчатку глаза. Также появился стандартный системный UI для запросов биометрической авторизации.
Dynamic Delivery. Сейчас, с развитием альтернативных сторов, я понял, какая хорошая вещь App Bundle и Dynamic Delivery. Суть в том, что нашему устройству не нужны все ресурсы, а значит, логично доставлять только те, что нужны, и не качать лишнее по сети.
Структура Android-приложения, загружаемого через App Bundle
Кроме этого, появилась крутая штука — динамические фичи. Отдельные части приложения можно закачивать динамически по необходимости.
Slices. Это возможность интеграции богатых виджетов с Google Assistant.
Если вы ищете что-то в Google Assistant и ваше погодное приложение стоит на устройстве, можно не просто дать ссылку, а вставить богатый виджет.
Android 10
Началась печальная эра, потому что эта версия называется просто Android 10 — и все, без «десертного» псевдонима.
Тип Foreground Service. Продолжилась работа с Foreground Service. Мало того что их ограничивали, так теперь им еще и дали четкие категории по целям использования:
-
camera — использует камеру или записывает видео;
-
connectedDevice — работает с Bluetooth-устройством, авто и другими;
-
dataSync — передача данных по сети, бэкап и восстановление, загрузка файлов;
-
location — GPS, карта или навигация;
-
mediaPlayback — проигрывание звука и видео;
-
mediaProjection — управление проекцией медиа (запись видео с экрана, создание скриншота и прочее);
-
microphone — использует микрофон или записывает аудио;
-
phoneCall — текущие операции, связанные с телефонными и видеозвонками либо схожими коммуникациями.
Важно четко отметить у сервиса его тип. Один сервис может иметь несколько категорий, но все они должны быть объявлены заранее. Если вы попытаетесь запустить сервис категорий, который не внесли, случится сбой. Запустить сервис с одной категорией, но делать там что-то другое нельзя. Система четко отслеживает, что вы вызываете, и может пригрозить вашему приложению и ограничить его за нарушение правил.
ACCESS_BACKGROUND_LOCATION. Теперь нужно запрашивать не только доступ к местоположению, но и доступ к нему в фоновом режиме.
Project Mainline. Cамая важная веха в избавлении от влияния вендоров. Вообще, Project Mainline можно считать развитием Project Treble.
Структура Android до версии Android 10 выглядела так:
Весь Android OS Framework мог обновляться и доставляться вендору, а вендор доставлял его на устройство. Здесь есть непонятное промежуточное звено: если я обновляю фреймворк Android, зачем мне вендор, который это доставит?
От посредника решили избавиться.
Часть модулей операционной системы выделили в отдельные блоки, которые спокойно можно подменить. А обновлять их решили через Google Play.
Сейчас таких модулей около 40, на изображении выше перечислены основные. Например, в последней версии Android даже ART VM будет обновляться независимо от производителей. Прослеживается посыл Google, что они хотят обходиться без вендоров.
Jetpack Compose. Современный набор инструментов для создания декларативного UI в Android. Тут без комментариев: вещь хорошая, и для Android-разработки он будет важен ближайшие лет 10.
Coroutines First. В Android впервые появился официально рекомендованный асинхронный подход на корутинах при условии, что вы используете Android Jetpack.
Android 11
Scoped Storage. Эта шутка появилась в Android 10, но я отнес ее к нововведениям Android 11. В Android 10 она была забагована, а в Android 11 ее включили по умолчанию. Если раньше приложение могло получить доступ к Extended Storage, записывать и читать все что угодно, теперь так нельзя.
Это напоминает жесткость iOS. Но iOS, наоборот, уже изменила позицию и дала пользователям залезать в файл.
One time permission. Ввели «разрешения на один раз». К сожалению, в коде разработчиков видел, что, если пользователь однократно дал им разрешение, они считают, что разрешение есть всегда. Но нет.
Автоматический отзыв разрешений. Это первая функция, которую полноценно портировали на все старые версии Android. В следующем году все устройства с Android 6.0 и выше будут иметь автоматический отзыв разрешений. Хотя это вроде бы часть Android 11, поддержка старых версий Android с новых будет.
Ограничение на видимость пакетов. Направлено на тех, кто любил собирать нашу информацию, понимать, какие приложения мы устанавливаем, и кому-нибудь это продавать.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
<queries>
<!-- Указаний спец пакетов по которым можно запросить инфо -->
<package android:name="com.mobiusconf" />
<package android:name="dev.androidbroadcast" />
<!-- Специальный intent-ы для запроса -->
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="image/jpeg" />
</intent>
<!- Получение информации о пакетах,
которые содержат Content Provider с authority -->
<provider android:authorities="content://com.mobiusconf/speakers"/>
</queries>
</manifest>
Теперь в манифесте вы должны явно указать, у каких приложений будете просить информацию. Есть несколько вариантов, как это сделать.
Способ получить информацию обо всех пакетах остался: можно запросить специальное разрешение. Казалось бы — просто запроси все. Но с этим разрешением нельзя просто проехать в Google Play. Он скажет: докажи, что это разрешение тебе нужно.
Это один из первых случаев, когда Google Play стал второй линией обороны в плане того, что разработчик может делать в своих приложениях. Если это, например, будильник, будет странно, если создатель приложения захочет узнать, что еще загрузил пользователь. Надо доказать, зачем это нужно.
Window Insets Callback. Появилась вещь, которую все давно ждали. Раньше при открытии клавиатуры менялась только высота экрана, а теперь на это можно реагировать полноценно. API работы с клавиатурой и понимания того, что открыто, мы так и не получили, хотя просили именно этого.
Android 12
Material You (он же Material 3)
Оставлю без комментариев. Кому-то нравится, а кому-то — нет.
Restricted Standby Bucket. Теперь, если система видит, что разработчик слишком много что-то делает или делает что-то ненужное, его занесут в этот бакет. А он ограничивает практически все. Можно сказать, что это мини-тюрьма для приложений.
Battery Usage. Кстати, вы можете понимать, в каком бакете находитесь, когда приложение запускается. Можете попросить пользователя что-то сделать с вами и для этого появляется новый раздел Battery Usage. Restricted-настройка, которая вроде бы работает автоматически, но позволяет пользователю перенести вас туда вручную.
В настройках любого приложения можно выбрать три опции:
-
optimized (по умолчанию). Баланс между тем, как ваше приложение может работать в фоне, и тем, как оно выполняет нагрузку на батарею;
-
unrestricted — сказать приложению «делай что хочешь»;
-
restricted — насильное отправление в Restricted App Standby Bucket.
Ограничения Restricted-режима:
-
не может запускать Foreground Service;
-
запущенные Foreground Service убираются из foreground;
-
Alarm не будет срабатывать;
-
Job не будет запускаться;
-
не будут доставляться бродкасты BOOT_COMPLETED и LOCKED_BOOT_COMPLETED (Android 13).
Сделать там практически ничего нельзя. Вы даже не сможете получить событие о том, что произошла загрузка устройства, и выполнить первоначальную настройку, чтобы приложение могло функционировать. Пока пользователь не будет явно запускать приложение, с ним ничего нельзя делать. Хорошая фича для того, чтобы заносить туда приложения, которые вы не любите.
Ограничения на запуск Foreground Service. В Google решили, что Foreground Services нужно запускать только явно, а именно теперь это может сделать только пользователь и только в явном виде. Есть несколько исключений, но они меняются от версии к версии. Фактически сейчас Foreground Service можно запускать только для тех операций, которые остались с Service Types, причем некоторые работают с ограничениями.
Разрешение SCHEDULE_EXACT_ALARM. Это разрешение появилось очень хитро. На самом деле это не разрешение, а специальный доступ. Для работы Exact Alarm нужно сказать пользователю: «Я дам тебе инструкцию, а ты сходи в настройки и сделай мне это». Фактически так уничтожили чужие будильники.
App Widget. Виджеты прокачали, но, по-моему, мертвого ничто не может спасти. Поправили косяки, о которых годами не вспоминали, добавили Jetpack Compose: есть специальная библиотека Jetpack App Glance, которая позволяет создавать виджеты в декларативном стиле.
class FirstGlanceWidget : GlanceAppWidget() {
@Composable
override fun Content() {
Column(
modifier = GlanceModifier
.fillMaxSize()
.background(ImageProvider(R.drawable.app_widget_background))
.appWidgetBackground()
.appWidgetBackgroundRadius()
.padding(16.dp)
) {
Text(
text = "First Glance widget",
modifier = GlanceModifier.fillMaxWidth().padding(bottom = 8.dp),
)
Row(…) {
Button(
text = "Button 1",
modifier = GlanceModifier.height(48.dp),
onClick = actionStartActivity<MainActivity>()
)
Button(…)
}
}
}
}
О них вспомнили, потому что годом ранее в iOS выстрелили виджеты. Это не давало покоя Google, и они решили make widgets great again. Вышло так себе :(
Expedited Job. Он остается центральным местом выполнения всего, поэтому его стали прокачивать. Но у пользователей все равно остаются задачи, которые нужно выполнять моментально.
Для этого ввели новый тип Job — Expedited. Это высокоприоритетные задачи, которые выполняются без отлагательств. Система выделяет квоту одновременно запущенных Expedited Job для приложения, а активное приложение может запустить их сколько угодно. Важно понимать, что если вы превысите лимит, то старт такой Job будет отложен. Выполнить таким образом долгие задачи не получится. Максимальное время для такой Job — 60 секунд, но иногда оно может продлеваться, если есть ресурсы.
App Search. Улучшили механизм поиска, то есть создали поисковый движок, специально оптимизированный под мобильные устройства и способный работать из коробки. Вот его особенности:
-
реализация быстрого хранилища с учетом особенностей мобильных устройств с низкой нагрузкой на I/O;
-
оптимизированная индексация;
-
быстрый поиск на больших массивах данных;
-
поддержка множества языков;
-
ранжирование на основе релевантности и частоты использования;
-
безопасный доступ для поиска по вашим данным из других приложений;
-
локальное (Jetpack) и платформенное (Android 12+) хранилище.
App Search классный. Если хотите делать поиск внутри приложения, обязательно обратите на него внимание.
Классы производительности. Классная фича. Кто задавался вопросом, как настроить что-то в приложении в зависимости от мощности устройства, на котором оно запущено? Если устройство было флагманом три года назад, можно ли считать его мощным сегодня?
В Google дали ответ на вопросы, выпустив Compatibility Definition Document. Это специальный документ, который задается производителям устройств при адаптации новой версии Android на эти устройства. Какое должно быть поведение, как нужно все сделать — все это есть в мануале.
Класс производительности теперь закрепляется с каждой версией Android. Например, появился Performance Class 12, что соответствует видению Google для устройства на Android 12, скажем так.
Если устройство обновляется и соответствует новому классу производительности, оно будет получать этот класс. Но устройство может не соответствовать требованию. Например, в Android 13 появится необходимость записывать 8К-видео. Если устройство не способно на это, оно не получит новый класс производительности.
Может быть две причины несоответствия:
-
ленивый вендор, который не хочет обновить константу у себя;
-
требования действительно начинают опережать время, но устройство все еще актуально.
when(val devicePerformance = DevicePerformance.create(applicationContext)) {
devicePerformance.mediaPerformanceClass >= Build.VERSION_CODES.S -> {
// Performance class level 12+
// Включаем всё на полную. Лучший уровень производительности здесь
}
devicePerformance.mediaPerformanceClass == Build.VERSION_CODES.R -> {
// Performance class level 11
// Высокий уровень производительность
}
else -> {
// Performance class level неизвестен
// Отключаем все улучшалки чтобы обеспечить хорошую работу приложения
}
}
Эта библиотека была перенесена и в Jetpack, поэтому можно получить класс в своем коде и как-то реагировать на это. Если у устройства низкий класс производительности, постарайтесь отключить сложные анимации и дайте пользователю спокойно работать. Приложение не должно тормозить на устройстве пользователя из-за того, что там красивая анимация.
Android 13
Android 13 вышла 17 августа 2022 года и привнесла ряд важных доработок системы.
Photo Picker. Это системный компонент для выбора фотографий локально и в облаке. Он позволит избавиться от разрешений для доступа к изображениям и видео. Проверять наличие доступа тут тоже будет Google Play. И с помощью Project Mainline эта фича будет портирована на Android начиная с версии 11. Это говорит о стремлении Google обновлять устройство независимо от вендоров и доставлять новые фичи приложения на старые версии Android.
Язык для приложения. Появилась возможность выбрать для каждого приложения отдельный язык. Важно: чтобы фича заработала, нужно указать это в коде приложения:
// xml/locales_config
<locale-config
xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="ru" />
<locale android:name="en" />
</locale-config>
// AndroidManifest.xml
<manifest>
<application
…
android:localeConfig="@xml/locales_config"
>
</application>
</manifest>
По умолчанию фича не работает, ведь приложение не поймет само, какие языки для него нужны.
Notification Permission. Для показа уведомлений нужно будет запросить разрешение у пользователя. В iOS это всегда так и работало, теперь появилось и на Android.
Foreground Service Task Manager. Благодаря этому таск-менеджеру пользователь может заметить, что приложение ведет какую-то подозрительную работу, просмотреть список таких приложений и нажать кнопку «Остановить».
Остановка там довольно жесткая: нечто среднее между удалением из Recent и применением Force Stop. Удалится весь интерфейс, будет убито все, что свернуто.
Разрешение USE_EXACT_ALARMS. Первое нужно было ходить запрашивать через настройки, а второе — штука, которая выдается автоматически. Тумблер будет выставлен по умолчанию. Вроде бы все хорошо. Но если первое может запросить любой, попросив пользователя выбрать это, то во втором случае нужно получить разрешение у Google Play на публикацию с таким разрешением.
Если приложение — не будильник, календарь или прочее, его нельзя опубликовать с таким разрешением.
Программируемые шейдеры. Если кто-то очень хотел сделать блюр, как в iOS, радуйтесь. Теперь есть легкий API, который может это сделать без жесткой нагрузки на устройство. Только менеджерам это не рассказывайте, пожалуйста.
Поддержка больших экранов. Вообще, поддержка больших экранов была уже в Android 3.0. Но все новое — хорошо забытое старое, и Google решил make tablets great again. Речь не только про планшеты, но и про хромбуки и поддержку Android-приложений в Windows.
Тенденции развития Android
Резюмируем сказанное выше и выделим главные тенденции:
-
меньше возможности работы в фоне без контроля пользователя;
-
отказ от вендоров как необходимого звена в обновлениях;
-
дополнительный барьер для приложений через Google Play;
-
упор на безопасность и приватность данных;
-
Jetpack становится основой разработки под Android;
-
все больше сходства с принципами iOS.
Картина примерно была такой:
Теперь операционные системы понемногу приближаются друг к другу.
Как близко они подойдут друг к другу, я не знаю, но тенденция усиливается.
От редакторов: на Mobius и раньше можно было увидеть доклады Кирилла — скажем, в предыдущий раз он выступил с провокационной темой «Мобильной разработки не будет через пять лет». А сейчас мы готовим следующий Mobius, который пройдет 9—10 ноября онлайн и 21 ноября в Москве и онлайн). Все подробности и билеты — на сайте.
Спасибо команде JUG Ru Group и Тинькофф за помощь в работе над текстовой расшифровкой доклада
Автор: Кирилл Розов