- Как Android пришел к AAB? Что будет с APK? Разбор
- Проблема Android
- ЭТАП 1. Dalvik: Android тормозит
- ЭТАП 2. ART: Android потребляет
- ЭТАП 3. Profiling: Android оптимизируется
- ЭТАП 4. AAB: Android балансирует
- Выводы
- Как работает SystemUI в Android
- Данное приложение выполняет весьма важные функции:
- Запуск SystemUI
- Регулирование громкости
- RingtonePlayer
- PowerUI
- Задачи
- Главные функции:
- Экран блокировки
- Панель уведомлений
Как Android пришел к AAB? Что будет с APK? Разбор
Не так давно прогремела новость о том, что Android отказывается от APK-файлов и переходит на AAB. Вы наверняка уже эту новость прочитали, во всём разобрались и успокоились, так как новость проходная. Тем не менее, мы считаем, что переход к новой системе публикации приложений App Bundle — это часть большого пути, которую проделала система Android, чтобы стать по-настоящему быстрой, эффективной и супероптимизированной платформой. Поэтому мы подготовили большой и очень интересный материал. И сегодня мы раскроем вам массу страшных тайн Android.
Проблема Android
У системы Android есть одна очень важная особенность, которая является как огромным преимуществом, так и огромной проблемой.
Система Android должна быть универсальной. Это значит:
- Система должна работать на всём и вся: разные архитектуры, типы устройств и прочее.
- При этом под неё должно быть просто писать код. Чтобы один раз и везде.
- И, плюс ко всему, система не должна быть прожорливой и должна быстро работать хоть на ноунейме за 3 копейки, хоть на флагмане за 3 зарплаты.
Требования к Android:
- Поддержка разных архитектур и типов устройств
- Простота программирования
- Минимальные системные требования
- Высокая скорость работы
Итого, целых четыре требования. Но соблюсти все эти требования одновременно практически нереально.
Поэтому вся история развития системы Android — это история борьбы, компромиссов и поиска баланса.
Условно историю Android можно поделить на 4 этапа: когда Android тормозил, много жрал, оптимизировался и, наконец, находился в балансе.
Этап 1. Dalvik: Android тормозит
Этап 2. ART: Android потребляет
Этап 3. Profiling: Android оптимизируется
Этап 4. AAB: Android балансирует
И сегодня мы поговорим про все четыре этапа. Но начнём с небольшой ремарки.
Чтобы соблюсти первые два базовых требования к системе, а именно: поддержка разных архитектур и простота программирования. В качестве основного языка программирования в системе Android была выбрана Java. Почему так?
У Java есть несколько классных свойств: он изначально был создан как мультиплатформенный: пишешь один раз — работает везде.
Но, есть и недостаток. Достигается это всё очень грязными методами, а именно при помощи виртуальной Java-машины. Тут стоит пояснить.
Дело в том, что люди пишут код на понятных для человека языках программирования, но машины не понимают такой код, они понимают машинный код, то есть бинарный код. А значит, в процессе финальной сборки приложения, человеческий код нужно перевести в машинный код. Такой процесс называется компиляцией. А приложение скомпилированное под требуемую архитектуру называется нативным.
Нативные приложения — самые быстрые, потому они записаны на языке понятном железу. А теперь смотрите внимательно: приложения написанные на Java компилируется не в нативный код, а в промежуточный код, который называется байт-кодом.
А вот уже из этого байт-кода можно достаточно быстро перевести приложение под любую архитектуру при помощи виртуальной Java-машины.
Иными словами, приложения написанные на Java — это всегда не нативные приложения, которые требуют дополнительной пост-компиляции., а значит дополнительных издержек.
И Android-приложения — не исключение.
Единственное что, в Android вместо виртуальной машины Java используется собственная, куда более эффективная, виртуальная машина Dalvik или ART. А также вместо байт-кода Java используется собственный, куда более эффективный, байт-код, который внутри APK-шек записыватся в файлах с расширением DEX (анимация).
Тем не менее, это не меняет сути, т.к. Android-приложения содержат много Java-кода и это проблема, которую как-то нужно решать. Так вот на протяжении своей истории эта проблема решалась по-разному.
ЭТАП 1. Dalvik: Android тормозит
Вплоть до Android версии 4.4 KitKat приложения запускались через виртуальную машину Dalvik, которая работала по принципу Just In time компиляции или JIT-компиляции. То есть приложения транслировались в нативный код прямо во время исполнения, то есть “на лету”.
Мы уже рассказывали про JIT-компиляцию в ролике про Android на Windows 11, если не видели — посмотрите. Е
Так вот, естественно такая компиляция на лету — не оптимальный подход:
- Приложения запускаются дольше
- Работают медленнее
- Потребляют больше энергии
Короче, сплошные минусы! Но зачем тогда так нужно было делать?
Ответ простой: такой подход позволял экономить много памяти — в первую очередь, оперативной. Тогда устройства были не такие мощные как сейчас, у многих на борту было не больше 200 Мб ОЗУ. А JIT-компиляция позволяла, так сказать, загружать в оперативку только ту часть приложения, которая используется.
Плюс ко всему, сам по себе байт-код компактный, поэтому и на диске приложения занимали очень мало места.
Да и были возможности оптимизации: скомпилированный код можно записать в кэш и дальше уже из кэша брать как бы нативный код. В общем, можно жить…
Помните же Dalvik-кэш? Вот это как раз он…
ЭТАП 2. ART: Android потребляет
Тем не менее пользователи и приложения становились всё более требовательными к отзывчивости. И в Android 4.4 KitKat была представленная новая виртуальная машина ART или Android Runtime. А в Android 5.0 Lollipop ART полностью заменила Dalvik.
Вместе с новой средой выполнения, Android поменял стратегию на 180 градусов. Вместо компиляции во время исполнения приложения ART стала использовать компиляцию перед исполнением. То есть компиляция теперь делается во время установки приложения. Такой вид компиляции называет Ahead Of Time компиляция или сокращенно AOT-компиляция.
И естественно, Android залетал! Приложения стали быстрее запускаться и работать без каких либо дополнительных задержек. Фактически все приложения внезапно стали “нативными” для железа!
Вообще согласитесь, Android 5-й версии был хорош. Система летала, представили Material Design… Просто счастье.
Но вы же понимаете, что на этом развитие Android не остановилось. Ведь у чистой AOT-компиляции есть недостатки, а именно:
- Сильно увеличился вес приложений, ведь машинный несжатый код весит много.
- Увеличилось время установки приложений, ведь компиляция происходила именно в момент установки.
- При каждом обновлении системы приходилось перекомпилировать все приложения.
Помните вот эту бесконечную оптимизацию приложений после установки ежемесячного обновления безопасности системы? Вот это оно — AOT-компиляция во всей красе.
ЭТАП 3. Profiling: Android оптимизируется
Поэтому в Google подумали: компилируя приложение целиком, не делаем ли мы лишнюю работу? А вот и делаем!
Как выяснилось, по статистике пользователи очень редко используют более 10-20% кода приложения. Иными словами, в большинстве случаев заранее будет достаточно скомпилировать только малую часть, которая будет действительно использоваться часто, а для редких уголков приложения, в которые мы не заходим, можно будет и JIT-компиляцию использовать.
Но вот только, какую часть кода нужно скомпилировать заранее, ведь даже разработчики приложений не знают, как именно пользователи будут юзать их программу.
Поэтому в Android 7.0 Nougat Google представили технологию PGC — Profile guided compilation. То есть это компиляция, основанная на профилях использования приложения. Думаю вы уже примерно догадываетесь как эта штука работает.
Естественно от AOT-компиляции на этапе установки отказались. Поэтому во время первого запуска приложения стали снова использовать старую добрую JIT-компиляцию, результат которой, естественно, сохранится в кэш. Тоже самое повторится и во время второго запуска, и третьего. Но когда вы поставите телефон на зарядку и крепко заснете, тогда проснется так называемый «Демон» (это, если что, официальное название специальной службы), который проанализирует кеши всех приложений, которые вы использовали в течение дня. После этого он создаст профили с оптимизированным кодом. И так каждую ночь…
Такой подход нивелировал недостатки чистой AOT-компиляции:
- Сильно ускорилось время установки приложений
- Ускорилось время обновления системы
- Скомпилированный код стал занимать на 80% меньше места на диске
В итоге сплошные плюсы за исключением одного очевидного жирного минуса: первое время приходилось терпеть тормоза.
Но и эту проблему Google решили. В Android 9.0 Pie они представили Облачные профили.
Они просто собрали профили приложений со всех пользователей, проанализировали их и создали усредненный профиль для каждого приложения, который теперь стал автоматически скачиваться во время установки приложения из Google Play Store.
Всё это позволило значительно повысить скорость первого запуска приложения, да и в целом, первые дни использования девайса. А, как известно, первое впечатление, второй раз произвести не получится.
И вот мы с вами видим какой огромный путь проделала система Android.
Система стала в десятки раз отзывчивее, оптимизированее и дружелюбнее к пользователям.
Но несмотря на огромное количество изменений под капотом самой системы оставалось одна очень важная проблема: огромное разнообразие устройств. Поэтому переходим к заключительному этапу становления Android.
ЭТАП 4. AAB: Android балансирует
На последнем этапе Google решил уменьшить не только размер скомпилированного кода, но и размер самих приложений.
И в 2018 году они представили новый формат публикаций приложений, который называется Android App Bundle, или просто AAB.
Это не новая технология. Всё это уже давно работает, но просто только сейчас она станет единственным правильным способом публикации приложений. В чём прикол?
Система Android поддерживает 4 архитектуры, 6 разрешений графики и более 150 языков.
Поэтому если собрать универсальный APK, который будет включать в себя вообще все необходимые файлы для всех девайсов (всю графику, все библиотеки, все языки) — такой файл будет просто неподъемно весить. А если собрать по идеальной APK-шке под каждое устройство, то придется генерировать тысячи таких APK.
Поэтому, чтобы разработчики не парились, Google придумал умную систему публикации.
Во время финальной сборки приложения они просто формируют бандл, то есть архив вообще со всеми необходимыми файлами под все девайсы. Делается это автоматически через Android Studio. И загружают этот архив в Google Play.
А дальше, когда вы заходите в Google Play и скачиваете приложение, то Google Play сам собирает для вас идеальную APK-шку только с необходимым набором данных: подгружается только графика необходимого разрешения, библиотеки только под вашу архитектуру и только тот языковой пакет, который выбран у вас в системе.
Кстати, да, если в системе у вас выбрано несколько языков, то подгрузится больше языковых пакетов. Вот так всё просто и умно.
Тем не менее, последствия от этого нововведения воистину колоссальные. Для Google это позволяет экономить ежедневно 10 ПБ трафика, который тратится на скачку и приложений и обновлений.
А для пользователей это позволят сэкономить просто кучу места на устройстве. Ведь многие приложения похудели более чем на 30 процентов.
Иными словами, от нововведения сплошные блага.
Те кто испугался, что APK больше не будет, это не так. Вы, по-прежнему, сможете скачивать и устанавливать APK-файлы с других источников, тут нет никаких ограничений. Точно также нет ограничений и на сторонние магазины приложений, они также смогут использовать App Bundle или по старинке заставлять разработчиков самим собрать APK. Полная свобода и анархия.
И в качестве финального аккорда. Если вам кажется, что все эти оптимизации, связанные с компиляцией, скоростью загрузки приложений, размером APK и вещи, которые проделал Google с Android, это всё фигня. В качестве аргумента что это не так, мы решили по приколу сравнить размер приложений на Android и iOS и вот, что обнаружили.
Размер приложения Facebook на iOS 246 МБ, на Android — 57. Разница в 4,3 раза!
Instagram. iOS 150 МБ, Android — 39, разница 3.8 раза!
Snapchat 234 против 63 МБ.
TikTok 230 против 67 МБ… и так далее.
В итоге только на выбранном небольшом списке приложений мы получили экономию, более чем в 1 ГБ! Мы считаем — это достойно, именно поэтому Android настоящая народная ОС.
Выводы
Что в итоге. За время своего существования система Android прошла просто огромный путь оптимизации и стала по-настоящему универсальной, быстрой и эффективной системой, которая отлично работает на массе разных устройств. Вот бы все ОС так развивались. (Да, Microsoft?)
А что касается второй и моей, уж чего греха таить, любимой системы, которая также прекрасно работает на разных устройствах. О ней поговорим в другой раз.
Источник
Как работает SystemUI в Android
В этой статье я разберу архитектуру и принцип работы основного приложения Android — SystemUI. Меня заинтересовала эта тема, потому что мне интересно, как устроена система, которой пользуется такое огромное количество пользователей и для которой ежедневно выкатываются тысячи приложений в Google Play или просто на просторы интернета. Помимо этого меня интересует вопрос информационной безопасности Android и создаваемых под него приложений.
В системе Android, SystemUI — это приложение, путь к исходному коду которого находится в platform_frameworks_base/packages/SystemUI/, на девайсе оно находится в system/priv-app/-SystemUI.
priv-app — это каталог, где хранятся привилегированные приложения. К слову, по пути system/app лежат предустановленные приложения, а обычные приложения, которые мы устанавливаем на свой девайс самостоятельно, хранятся в data/app.
Тут сразу возникает вопрос: почему нельзя засунуть все предустановленные и привилегированные приложения в один каталог, зачем нужно это разделение?
Дело в том, что некоторые приложения более системные, чем другие:) И это разделение необходимо для того чтобы уменьшить покрытие эксплойтами системных приложений, для получения доступа к защищенным операциям. Можно создавать приложение, которое будет иметь специальный ApplicationInfo.FLAG_SYSTEM и в системе получит больше прав, однако apk файл с таким разрешением будет помещен в раздел system.
Итак, SystemUI — это apk-файл, который по сути своей обычное приложение. Однако, если посмотреть на сложное устройство SystemUI, перестает казаться, что это всего лишь простое приложение, верно?
Данное приложение выполняет весьма важные функции:
Запуск SystemUI
Как я и говорила выше, SystemUI не похож на обычное приложение, так что его запуск не сопровождается запуском активности, как это происходит у большинства приложений. SystemUI — это глобальный пользовательский интерфейс, который запускается во время процесса загрузки системы и не может быть завершен.
Если мы залезем в SystemServer, который является одним из двух столпов в мире Android (второй — Zygote, но об этом я расскажу как-нибудь в другой раз), то мы можешь найти место, где стартует SystemUI при загрузке системы.
Тут мы видим как запускается сервис SystemUI с помощью непубличного API startServiceAsUser. Если бы вы захотели использовать это, то вам пришлось бы обратиться к рефлексии. Но если вы решите использовать reflection API в Android — подумайте несколько раз, стоит ли это того. Подумайте раз сто:)
Итак, тут создается отдельный процесс для приложения и по факту каждый раздел SystemUI является отдельным сервисом или независимым модулем.
Метод start() вызывается для запуска каждой службы, которые перечислены ниже.
Регулирование громкости
Мы регулярно пользуемся кнопками громкости на своих устройствах, но не задумываемся какие процессы должны произойти в системе для того чтобы мы могли прибавить или убавить звук. Операция кажется довольно простой на словах, но если заглянуть в VolumeUI, который находится в подпапке SystenUI/volume, в разных режимах интерфейс имеет свою вариацию.
Я уже говорила о том, что сервисы SystemUI запускаются методом start(). Если мы посмотрим на класс VolumeUI, то он тоже наследуется от SystemUI.
Тут мы видим что с помощью mEnabled мы определяем, следует ли нам показывать панель с настройкой звука. И судя по VolumeDialogComponent, VolumeUI отображает звуковую панель в виде диалога. Но все действия относительно нажатия на клавиши громкости обрабатываются в PhoneWindow.
Насколько мы видим, KEYCODE_VOLUME_UP (+) не обрабатывается и перейдет в обработку KEYCODE_VOLUME_DOWN (-). И в обоих событиях, как в onKeyDown, так и в onKeyUp вызывается метод dispatchVolumeButtonEventAsSystemService.
Итак, тут у нас вызывается метод adjustVolume, для того чтобы мы могли проверить наш direction, которому будет присвоен параметр события.
В итоге когда мы доберемся до AudioService, где будет вызван sendVolumeUpdate, где помимо вызова метода postVolumeChanged, будет установлен интерфейс HDMI.
RingtonePlayer
RingtonePlayer в Android выполняет роль проигрывателя. Он так же наследуется от SystemUI и в методе start() мы видим:
Здесь у нас устанавливается mCallback, который по сути является экземпляром IRingtonePlayer.
В итоге можно управлять RingtonePlayerService с помощью Binder для воспроизведения звуковых файлов.
PowerUI
PowerUI отвечает за управление питанием и уведомлениями. Аналогично наследуется от SystemUI и имеет метод start().
Как мы видим из приведенного выше кода, происодит подписка на изменения Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, а после — вызов mReceiver.init().
Тут регистрируется широковещательный приемник, с помощью которого происходит отслеживание изменений.
Задачи
Recents — это основная и часто используемая функция в мобильных устройствах на базе Android.
Главные функции:
- Отображение всех задач
- Переключение между задачами
- Удаление задач
Помимо этого Recents так же наследуется от SystemUI. В RecentsActivity происходит создание и обновление последних задач, чтобы мы могли увидеть их на нашем экране.
А в с помощью RecentTaskInfo мы можем получить информацию о конкретной задаче.
Вообще, запущенные задачи можно вынести в отдельную тему. Я изучила ее со всех сторон, так как хотела размывать экран приложения перед переходом приложения в background, чтобы в RecentsTask отображалась нечитаемая версия снапшота. Однако, проблема заключается в том, что снапшот приложения берется раньше, чем вызывается onPause(). Эту проблему можно решить несколькими способами. Либо выставлять флаг, чтобы система просто скрывала содержимое экрана с помощью
О чем я говорила в предыдущей статье, посвященной как раз снапшотам.
Можно вообще сделать так, чтобы конкретная activity приложения не отображалось в задачах, проставив в манифесте
Либо можно воспользоваться хитростью с помощью
Можно задать основной активности выше приведенный флаг excludeFromRecents = true, для того чтобы ее экран отсутствовал в запущенных задачах, но во время загрузки приложения запустить отдельную задачу, которая будет показывать либо размытый скриншот с основной активности, либо любое другое изображение. Более подробно, как это можно сделать описано в официальной документации на примере Google Drive.
Экран блокировки
Keyguard уже посложнее всех вышеприведенных модулей. Он представляет из себя сервис, который запускается в SystemUI, а управляется при помощи KeyguardViewMediator.
Однако на самом деле KeyguardService самостоятельно не работает с интерфейсом экрана блокировки, он лишь передает информацию в модуль StatusBar, где уже и производятся действия относительно визуального вида экрана и отображения информации.
Панель уведомлений
SystemBars имеет довольно сложное устройство и структуру. Его работа разделяется на два этапа:
- Инициализация SystemBars
- Отображение уведомлений
Если посмотреть на запуск SystemBars
То мы видим ссылку на ресурс из которого читается имя класса и создается его экземпляр.
Таким образом мы видим что тут вызывается StatusBar, который будет работать с выводом уведомлений и UI.
Я думаю никто и не сомневался в том, что Android устроен очень сложно и заключает в себе много хитростей, которые описаны в огромном количестве строчек кода. SystemUI является одной из самых важных частей этой системы и мне понравилось изучать ее. Из-за того что материала на эту тему очень мало, если вы заметите какие-либо ошибки, прошу исправить меня.
Источник