Привет! Я Александр, старший Android-разработчик в Авито, ранее работал в Яндексе и Kaspersky. Пишу о разработке и всём, что с ней связано, в своем телеграм-канале. Сейчас поговорим немного про главный метод и про процесс Android-приложения.
Если вы когда-нибудь писали «Hello World» на Java (или любом другом C-подобном языке), то наверняка делали это в статическом методе main. Этот метод вызывается в JVM и является точкой входа любой Java программы.
public static void main(String[] args)
Но при разработке приложений для Android вы возможно, как и я до недавнего времени, ни разу метод main не встречали. Кажется будто его вообще не существует, а при нажатии на иконку в лаунчере наши Application и Activity создаются какими-то волшебными гномами, спрятанными в устройстве.
На самом деле метод main в Android все-таки есть, и содержится он в классе ActivityThread. Но давайте по порядку.
ActivityThread
Что это за зверь такой? Неужели каждая Activity работает на особом потоке? Конечно нет, просто ActivityThread‑ом разработчики Android SDK решили назвать главный (Main) поток приложения. Да, нейминг вводит в заблуждение, но так уж повелось ¯\_(ツ)_/¯
Теперь — что же делает метод main в ActivityThread‑е. Тут всё прозаично: он настраивает главный поток и инициализирует его Looper. Проще говоря, на главном потоке запускается бесконечный цикл, в котором и выполняется код приложения — создаются Activity, отображаются View и т. д. Про Looper уже написано множество статей, не буду пересказывать их — подробнее можно почитать, например, тут и тут.
Собственно, так я и наткнулся на объявления метода main — лазил по сорсам Looper‑а и решил найти место, где инициализируется MainLooper. Но дальше может возникнуть вопрос — кто же вызывает метод ActivityThread.main при запуске приложения? И тут в дело вступает...
Zygote
Вы точно встречали это название, когда смотрели логи приложения. Zygote — это процесс, который является базовым для всех приложений в системе. Само по себе название очень четко отражает суть: в биологии зиготой называется клетка, образующаяся в результате оплодотворения, и способная породить любую другую, копируя себя.
Наш же Zygote запускается при старте системы, загружает системные библиотеки и фреймворки и входит в бесконечный цикл, который ожидает команды на запуск приложений.
При запуске очередного приложения Zygote создает полную копию себя, которая и будет являться процессом данного приложения. Делает он это с помощью системного вызова fork. При этом оригинальный Zygote продолжает крутить бесконечный цикл, а вот его копия выходит из цикла и вызывает метод ActivityThread.main, тем самым запуская приложение.
Вызов fork в ядре Linux имеет облегченную реализацию — он не создает копию памяти для нового процесса, а просто переиспользует существующую. Такой механизм позволяет выполнять приложения в отдельных процессах, не тратя каждый раз время на загрузку или копирование классов.
Заключение
Итак, главный метод в Android-приложениях хоть и скрыт от наших глаз, но играет очень важную роль в системе. Он находится в классе ActivityThread, запускается Zygote и сам запускает MainLooper приложения.
Механизм работы Zygote позволяет оптимизировать запуск приложений, но обладает и недостатками. Например — раз этот процесс является базовым для всех приложений, то можно заразить его трояном, который затем прорастет в каждое приложение. Так работает один из самых страшных троянов в Android — Triada. А делать он может всё что угодно — например, перехватывать и читать ваши смс. Вывод банальный — не стоит устанавливать на устройство приложения из сомнительных источников.
Комментарии (3)
LuigiVampa
30.10.2024 17:15Механизм работы Zygote позволяет оптимизировать запуск приложений, но обладает и недостатками. Например — раз этот процесс является базовым для всех приложений, то можно заразить его трояном, который затем прорастет в каждое приложение. Так работает один из самых страшных троянов в Android — Triada. А делать он может всё что угодно — например, перехватывать и читать ваши смс. Вывод банальный — не стоит устанавливать на устройство приложения из сомнительных источников.
Не только малварь, но и вполне популярные опенсорсные инструменты для динамической инструментации и модификации поведения андроид фреймворка используют внедрение в процесс приложения в момент форка зиготы. Это может быть полезно как для исследователей, так и просто для продвинутых пользователей андроида.
Исследователей интересует возможность заглянуть под капот работающим процессам, исследовать механизмы работы приложений, техники внедрения зловредов, и т.д. Продвинутых пользователей, например - дать щелбан по носу чересчур любопытной операционной системе и системам трекинга и аналитики, собирающим и отправляющим 24/7 информацию о каждом чихе сделанном пользователем на устройстве, которые сейчас встроены в 99% андроид приложений, или починить в системе что-то, что Гугл упорно не хочет исправлять, например систему пермишенов, или никогда не будет исправлять, например то, что помогает им собирать информацию о владельце устройства. Ключевая идея - иметь возможность выполнить произвольный код или переопределить поведение интерфейса системы ДО передачи управления запущенному процессу.
Единственный нюанс - заинжектить некую кастомную логику ДО форка процесса без рута или хотя бы разблокированного загрузчика и отключённой доверенной загрузки не получится, т.к. это требует изменений на уровне системы. Все популярные инструменты для управления рут доступом имеют модули, которые позволяют удобно организовать внедрение в зиготу, но у обычного непривелегированного приложения так сделать не получится.
nikolz
Подобное и подробнее на хабре написали 7 лет назад
https://habr.com/ru/articles/345120/
vlaas22 Автор
Спасибо за ссылку! Действительно, довольно подробная статья. Там углубление в работу Looper (о чём я осознанно не стал писать в своей статье) и запуск Activity. У меня же больше про процесс Zygote и вызов метода main в нём. Надеюсь, статья вышла полезной!