Warning: Undefined array key 0 in /var/www/tgoop/function.php on line 65

Warning: Trying to access array offset on value of type null in /var/www/tgoop/function.php on line 65
152 - Telegram Web
Telegram Web
Баги
Баги, баги баги. Класс View развивался с самой первой версии Android, а это уже как 14 лет. Даже несмотря на это, во View все еще есть недочеты. Ожидать что Compose, которому всего пару лет, сразу будет работать идеально это довольно оптимистично. Естественно Compose еще поуши в багах, которые со временем (я надеюсь) пофиксят. Поэтому если и внедрять его в существующие проекты то очень аккуратно и желательно под каким-нибудь фиче флагом.

Гугл, который любит Deprecated
Это не относится напрямую к самому фреймворку Compose, однако частично его касается. Compose делают инженеры из компании Гугл. А Гугл славится чем, что любит депрекейтить свои же решения. Реально, они в одном из релизов, задепрекейтили аннотацию Deprecated. Support Library, Billing Library, Synthetics, Volley (формально она не deprecated, но ее поддерживает всего один разраб, а пользуются еще 2.5, что не особо вдохновляет) это те немногие решения от которых Гугл просто отказались.

Разумеется нет никаких оснований полагать, что от разработки и поддержки Compose откажутся просто так. Однако нужно понимать, что Compose закрытый проект, который поддерживает одна компания и в случае чего будет достаточно трудно с него слезть. Будет конечно сюр, если они через пару лет выпустят ComposeX и скажут, все что было до этого deprecated, пожалуйста, не используйте.

Производительность 
Один из плюсов Compose по обещаниям разработчиков это производительность. В целом так оно и есть, благодаря подходу позиционной мемоизации (поставьте пару сердец если хотите про это отдельный пост). Compose функции и правда очень производительны и перерисовывается только малая часть экрана которая изменилась.

Однако ходят слухи о том, что в Compose тормозят списки. Списки действительно тормозят, но только в debug сборке, потому как в релизной сборке включается куча оптимизаций про которые забывают. Некоторые разработчики проводили тесты которые показывают что, Compose и правда медленнее текущего решения примерно на 10% в релизной сборке, что для нового фреймворка терпимо. 

В итоге можно сказать, что Compose существенно не влияет на производительность. Однако в дебажной сборке он будет безбожно тормозить. Помимо этого плохая идея смешивать RecyclerView и Compose, вот тут оптимизации идут лесом и списки правда будут тормозить даже в релизной сборке. Если решили делать списки на Compose, то делайте их полностью на Compose.

Диалоги
Вот тут пока грустно. Если посмотреть на базовый пример того, как показываются диалоги. Решение выглядит мягко говоря костыльно. Нельзя как раньше сказать системе покажи диалог и забить на него. 

Сейчас показывая диалог вы должны полностью контролировать его показ, он как бы становится частью вашей верстки. Диалог в Compose обязывает создавать состояние, чтобы контролировать показ этого самого диалога. Удобно это или нет, уже решать вам.

Скоуп ViewModel
С ViewModel тоже не все однозначно. Изначально ViewModel проектировались именно под скоуп фрагмента или activity, другими словами ViewModel умирали со смертью компонента в котором используются. Фишка в том, что если мы используем Compose без фрагментов или activity у нас нет компонентов, которые бы позволяли отлеживать ЖЦ. Точнее говоря он есть, та самая первая Acitivty с которой начинается запуск Compose и если вы будете просто использовать ViewModel они будут жить пока не умрет та самая первая Activity. Вот только она нихера не умрет, пока приложение используется и это может доставить гемороя.

Частично эту проблему решили. Однако решили тем, что вы обязаны использовать библиотеку навигации от гугла, если хотите что-то другое, то делайте все на фрагментах. Вот такой тоталитаризм от гугла.

P.S прикиньте в телеге есть пользователь с ником “Deprecated” и если писать эту аннотацию с @ то телега считает что я указываю именно его. Забавно, вот у него наверное оповещений навалом с разных каналов про разработку)

UPD: Compose не закрытый проект, а open source тут я вас обманул. Однако если Гугл откажется от его поддержки не факт, что кто-то возмется
46👍5
Хватит делать проксирующие интеракторы

👍15😁8👏3
Все слышали про Дядю Боба и его вклад в индустрию. Одна из известнейших его идей это Clean Architecture, которую сейчас используют оочень много проектов. В какой проект не приди там будет Clean, за некоторыми исключениями. На этот пост меня сподвигла ситуация, когда я собеседовал разработчика и при решении задачи с архитектурой, он сказал что всегда нужно делать Interactor, даже когда в них нет логики и они просто проксируют вызовы, на всякий случай.

Соль в том, что чем дальше двигается индустрия тем больше Clean Architecture обретает ореол сверхценной идеи. Если ты не используешь Clean значит твоя архитектура хрень и скорее всего проект не взлетит. Успех проекта зависит же исключительно от архитектуры!

Мы стали забывать, что Clean это лишь рекомендация человека, который был простым разработчиком. Да с большим опытом, да очень медийным, но все равно разработчиком. Людям склонно ошибаться, индустрия разработки это не математика, нельзя придумать формулу, которая будет одинакова везде. Каждый проект отличается.

В целом можно понять почему разработчики пытаются полностью следовать Clean и делают кучу интерфейсов. Всегда есть страх того, что вот сейчас придет заказчик и скажет поменять базу данных. А мы как раз готовились к этому всю жизнь, у нас все на интерфейсах и мы быстро мигрируем. Только вот прикол в том, что заказчик не придет с таким, особенно если речь идет о Android. Он максимум придет с предложением перекрасить кнопки в другой цвет. Мы защищаемся от изменений которые никогда не произойдут.

К чему все эти разглагольствования. Clean это лишь рекомендация по архитектуре, а это значит не нужно все делать именно так как описано в книге Мартина, можно и нужно отходить в сторону если считаете, что так будет удобнее. Даже используя Clean нужно задавать вопросы и не следовать слепо общему тренду.

Я не буду говорить как правильно делать архитектуру, этого никто не знает. Если человек говорит, что знает как сделать архитектуру которая всем поможет он пиздит. Однако я могу сказать что не нужно делать:

👉 Не нужно покрывать все и вся интерфейсами, если вы не подменяете это в тестах. Эти интерфейсы только мешают. У нас сейчас крутые IDE, которые если необходимо могут сами выделить интерфейсы когда это понадобится.
👉 Не нужно делать проксирующие интеракторы без логики. Нафиг эти интеракторы которые существуют просто потому что. Чем меньше кода, тем меньше багов.
👉 Не нужно спорить про разницу между Interactor и UseCase. Давайте начистоту, да никто не знает в чем разница. Даже сам Мартин уже не помнит этого. Не занимайтесь холиваром, лучше займитесь делом.
👉 Не нужно пытаться предсказать изменения и везде где можно постелить солому в виде абстракций. Вы не сможете предсказать изменения. Однако это тонкая грань, архитектура не должна ломаться от изменений в требованиях, но и не нужно пытаться предсказать все на свете.
🚫 Не нужно делать интерфейсы на интеракторы. Вот это вообще масло масленное. Довольно часто можно такое заметить. Интеракторы вы точно не будете заменять в тестах, это чистая бизнес логика. Если вам нужно заменить интерактор в тесте, значит интерактор делает то, что не должен!

Подводя итог, чтобы дать конкретные наставления. Если вы джун, строго следуйте Clean, так вы точно не натворите полной фигни. Если вы уже мидл или сеньор задумывайтесь о том, нужно ли вам строго следовать канонам или они только мешают.
👍495🔥1
В комментариях меня просили сделать посты про то, как стартует и в целом работает Activity со стороны JVM. Порой желание узнать как работает та или иная технология может завести довольно далеко. Поэтому я решил не ограничиваться только запуском Activity. Это будет достаточно длинная серия, из довольно хардкорных постов, посвящённых тому, как вообще работает Android.

В этой серии мы разберем:

👉 За что вообще отвечает ОС
👉 Что за sandbox
👉 Все ли сокеты используются только для сети
👉 Что такое Binder и нужно ли боятся AIDL
👉 Как мы получаем системные сервисы через Context
👉 Что такое Zygote
👉 Как система дергает методы ЖЦ Activity
👉 Путь от нажатия в лаунчере, до запуска приложения

Значит сразу, большая часть инфы этих постов вам скорее всего никогда не пригодится, разве что в будете в Гугл собесится. Однако зная как это работает под капотом сможете козырять на собесе и выбивать себе оффер по больше и выпендриваться на конфах и барах что я считаю святым вообще.
🔥68👍151
За что вообще отвечает ОС

👇
👍9🔥3
Вы задумывались об этом вопросе? Скорее всего некоторым повезло и препод с умным видом рассказывал вам про важность и устройство какой-нибудь ОС. Если кому не повезло как мне и вам вообще об этом не рассказывали, то присаживайтесь, давай-то по быстрому пройдемся по этому топику.

Итак, ответ в вкратце для чего нужна ОС. ОС – просто абстракция, чтобы когда вы писали программы вообще не думали о том, как ваша прога будет работать с железом. Представьте ситуацию, где вам потребовалось бы покрасить кнопку и вы держали в голове все порты монитора и думали о том, на какой порт подать напряжение чтобы кнопка покрасилась. Да еще и делать это нужно для каждого конкретного монитора. Жуть же правда, а именно так и было до создания ОС.

Теперь ответ не вкратце. Есть три пункта, выполнив которые мы можем сказать, что ОС состоялась:

👉 Планировщик процессов
👉 Менеджер памяти
👉 IPC (inter process communication)

Планировщик процессов это штука, которая вообще вводит понятие процесса. Планировщик решает сколько выдать процессу времени на процессоре, как распаралелить выполнение процессов и еще кучу всего связанного с безопасностью. Вы говорите ОС, хочу запустить эту прогу, а ОС уже сама создает новый процесс. Для этой программы все будет выглядеть так, будто ей доступно все железо на компе, хотя на само деле нет. Прям как вам говорят на входе в ВУЗ что у вас открыты все двери.

Менеджер памяти делает тоже самое что и планировщик процессов только с памятью. Этот менеджер следит за тем, чтобы какая-то одна прога не сожрала всю память, выделяет память под новый процесс, делает дифрагментацию, swap и вот это все. Когда мы выделяем память динамически, именно этот менеджер предоставляет нам память. Помимо этого он занимается виртуализацией памяти. Вашей программе кажется что ей доступна память оперативки в диапазоне ячеек с 0 до 1024, а на самом деле она работает в диапазоне 1024 по 2048. Это абстракция чтобы нам заранее не ломать голову, в каком участке памяти будет работать наша прога.

IPC это механизм межпроцессного взаимодействия. Не путайте с межпроцессорным, это две разные вещи! Так вот, этот механизм позволяет двум разным процессам общаться друг с другом. Фишка ОС в чем, что каждый процесс у нее это черный ящик, он вообще ничего не знает о других процессах и не может залезать в их память. Порой возникает необходимость поделится данными с другим процессом. Есть куча вариантов как передать данные другому процессу: через сеть, через файл, через буфер обмена или просто на прямую через оперативку. С файлом или сетью все довольно понятно, а через оперативку как, у нас же черный ящик? Вот именно безопасная передача данных между процессами напрямую через оперативку и есть головная боль IPC.

Естественно помимо эти трёх пунктов еще есть устройства ввода/вывода всякие драйвера, но эти три пункта прям обязательны и они самые важные.
🔥41👍10
Что за sandbox

🔥19
В предыдущем посте мы затронули тему планировщика процессов. Давайте пройдемся поглубже.

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

Тут можно затронуть тему отличия потока и процесса. Поток в некотором смысле это легковесный процесс. Суть очень похожа, несколько потоков также могут выполняться на разных процессорах параллельно. Разница в том, что потоки работают в рамках одного процесса или адресного пространства. Это значит что потоки могут довольно просто использовать общую память. В коде можно насоздавать потоков, которые работают с одной переменной. Процессы так не могут.

Механизм, который отвечает за безопасность, другими словами запрещающий процессам лезть в чужую память и файлы, а также пытаться прочитать данные пользователя без разрешения называется Application Sandbox. Application Sandbox не относится к самому Android, он работает на уровне ядра Linux.

Если вы работали с Linux вам должно совсем просто. Если нет, то вот как это работает. Каждому приложению выдается уникальный user Id или UID. Другими словами каждому приложению выдается свой пользователь. Это не реальный пользователь, а просто абстракция для системы упрощающая работу с правами. Каждый user id входит в какую-то группу. У каждой группы также есть уникальный id (GID). При установке приложения ему выдается UID и GID. При установке GID назначается первичный, т.е группа в которую входит user приложения при создании. Group это что-то вроде клуба. Каждый участник клуба может входить в несколько клубов. Group нужны для раздачи прав. Назначаем какие права конкретной группе и все участники получают эти права.

Естественно у UID которые выдаются обычным приложениям нет админских прав, значит они не могут сделать все что им вздумается. Системным приложения, такие как например контакты назначается привилегированный пользователь, т.е UID который изначально входит в специальную Group. Эдакий клуб джентельменов в который левых не берут. Когда пользователь выдает какие-то права приложению, например локацию. UID этого приложения помещается в Group, которой локация доступна. Далее когда ваше приложение через системный сервис пытается получить локацию, этот сервис проверяет UID этого приложение на наличие нужных прав, пытаясь найти этот UID в нужной Group.

Именно по это причине нежелательно рутовать устройство которым вы пользуетесь. Потому как в этом случае, у вашего пользователя (реального, не пользователя приложения) появляется возможность установить приложения которым похер на все ограничения. Такие приложения получают сразу привилегированного пользователя и могут творить что захотят без вашего ведома.

Жесткая аналогия, но я ничего не могу с собой поделать она мне нравится. Рутовать свое устройство это как брить яйца бритвой цирюльника. Да бритье более качественное, но есть риск того, что у вас довольно на долго упадет настроение.
👍24😁7🔥5
Все ли сокеты используются только для сети

👍81
Поговорим о демонах. В Unix системах есть программы которые работают в фоне со своей уникальной целью. По большей частью это служебные программы, написанные на низкоуровневом языке, гарантирующие работу какой-то подсистемы. Такие программы, написанные на низкоуровневом языке вроде Си называют демонами. Интересная история названия, почему именно демоны? Создатели Unix фанатели от греческой мифологии. В греческой мифологии Демон это персонаж выполняющий задачи, которыми не хотят заниматься боги.

Когда система стартует, она запускает кучу нативных демонов. Например, демон под названием netd, основная задача которого, следить за состоянием сети и сообщать об этом системному сервису. Определить демон ли программа можно по названию, у демонов есть следующая конвенция в названии {имя демона}d. Суть в том, что демоны предоставляют данные системным сервисам.

В предыдущих постах я упоминал системные сервисы. Что за системные сервисы? Вообще сервис как вы помните это основной компонент приложения. Это значит что приложение может быть лишь из одного этого компонента. Легко сделать приложение в котором будет лишь один сервис и ничего больше. Так вот в грубом представлении системные сервисы и есть такие приложения с одним сервисом.

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

В итоге мы имеем нативного демона который крутится в своем фоновом процессе, а также системный сервис работающий в другом процессе. Как передать данные из нативного демона этому сервису?

Вариантов куча, но сразу скажу что в Android используется Unix Domain Socket (UDS). Практически этот тот же самый сокет который мы используем для передачи данных по сети, но оптимизированный для передачи данных на одной машине. Этот сокет не использует сетевую карту, что избавляет от проверок IP и всего что связано с сетью. UDS для передачи данных использует файл. Это один из самых базовых способов передачи данных между процессами. Как понятно из названия эти сокеты есть во всех Unix системах, в том числе и в Android.

Однако у UDS есть два очень жирных минуса. Во-первых, способ достаточно медленный, т.к мы все данные передаем через файл, что несет свои издержки связанные с дисками. Во-вторых, способ нифига не защищенный, и это опять-таки из-за того что данные передаются через обычный файл. Конечно, для нативных системных сервисов это относительно безопасно, т.к обычное приложение не сможет прочитать из этого файла благодаря системе прав, о которой говорили ранее.

Поэтому для обычных приложений нет никакого смысла использовать UDS. Каким образом тогда у нас передаются данные между приложениями, и как мы получаем данные с системных сервисов которые работают в других процессах? Через Binder о котором поговорим далее.
👍18🔥81
Что такое Binder и нужно ли боятся AIDL

👇
🔥23
Итак, Binder. У нас задача передать данные между двумя процессами и желательно через оперативку, чтобы не было накладных расходов и все было безопасно. Для этого в Android был разработан Binder.

Binder это по сути фреймворк для построения межпроцессной коммуникации. Сам Binder работает на уровне ядра системы. Его фишка в том, что он позволяет описать интерфейсы на специальном языке AIDL. AIDL это что-то вроде супер урезанной Java. После чего при помощи кодогенерации сгенерировать классы Java. Дальше вы просто вызываете методы у Java классов как будто у вас все в одном процессе. По факту данные будут ходить между процессами.

Я не стал тут описывать как работает Binder и AIDL так уже делал это стародавних постах. В то время еще не было комментариев на канале и никто мне не сказал что вставлять смайлики в технический текст хреновая идея, поэтому уж извините. Короче, тут и тут самая важная инфа про AIDL и немного про Binder.

В этом посте я лишь сделаю пару важных дополнений. Безопасность в Binder обеспечивается тем, что в методе сервиса т.е в Stub можно получить UID и GID вызывающего приложения. Получив эти данные мы можем убедиться, что данное приложение действительно имеет разрешение на эти данные и все такое.

Вызывая методы на стороне клиента (Proxy) важно не забывать что они блокирующие! Сама коммуникация между процессами через Binder довольно быстрая, т.к все идет напрямую через память. Однако фиг знает что там будет делать сервис на той стороне. Что означает, что методы могут выполняться долго, поэтому желательно если вы вызываете методы через Binder убедится, что это не главный поток.

Что еще интересного у Binder, каждый раз на стороне Stub создается новый поток при вызове. Это значит что вы можете вызвать методы параллельно и на стороне сервиса они будут выполняться также параллельно.

Зачем про это знать. Скорее всего на практике вы с этим маловероятно столкнетесь, однако есть куча приложений и библиотек, где это используется. Самый яркий пример это Яндекс.Метрика. Когда вы затягиваете ее к себе, она стартует сервис в другом процессе, и вызывая методы метрики она передает данные через Binder.

Про Binder могут спросить на собесе, но обычно это уже уровень сеньора, поэтому просто знать как примерно это работает не повредит.
🔥32👍31
Как мы получаем системные сервисы через Context

👇
👍11🔥4
Итак, задумывались ли вы, что происходит когда вы достаете из Context скажем ConnectivityManager? Как вы помните ConnectivityManager это класс, который позволяет отлеживать состояние сети. У него достаточно удобное API позволяющее узнать если ли сейчас сеть, повесить колбеки на это, короче все что связано с сетью. Получаем мы его вот так:

val connectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager

И вот если вдуматься в синтаксис, ну абсурд же происходит! Вроде достаем сервис, а получаем менеджер. Да и системный же сервис работает в другом процессе, что за дичь вообще? Сейчас все раскидаем. Наше приключение начинается с Context. Два основных факта про Context. Первый – Context это God object умеющий все на свете и вообще связующий класс через который мы общаемся с системой. Второй – сам по себе Context это абстрактный класс.

У Context куча наследников, в том числе основные компоненты: Application, Activity, Service и т.д. В Context 200 методов и реализовывать их в каждом наследнике, учитывая то, что они делают примерно одно и тоже, это сизифов труд. Поэтому разработчики сделали класс ContextImpl, который реализует основные методы. Вспоминаем шаблон декоратор. Activity наследует Context, после Activity переопределяет методы Context которые ей нужны, а все остальные методы просто вызывают методы класса ContextImpl. Аналогично и во всеми остальными наследниками Context.

Нас сейчас интересует конкретный метод Context getSystemService. Каждый раз при вызове этого метода он идет в SystemServiceRegistry, который по сути: Map<String, (Context) -> Any>. И getSystemService делает примерно вот что:

fun getSystemService(str:Stiring): Any {
val fetcher = systemServiceRegistry[str]
return fetcher(this)
}


SystemServiceRegistry это класс, в котором огромный блок static наполняющий эту саму Map. Суть – есть имя сервиса и логика как его получить, обращаемся к SystemServiceRegistry по имени сервиса, и он нам его собирает. Точнее говоря, собирает не сам сервис, а некоторого клиента или менеджера который умеет работать с каким-то системным сервисом. Именно так мы и получаем ConnectivityManager, однако это только начало.

При старте системы, она запускает SystemServer, который в свою очередь запускает системные сервисы, прям все. Сервисов много, но нас сейчас интересует ConnectivityServiceInitializer. ConnectivityServiceInitializer это системный сервис, основная задача которого просто создать объект класса ConnectivityService и сохранить его у себя. Хоть ConnectivityService и называется сервисом, это просто обычный класс, в котором скрыта логика по общению с netd. С тем самым демоном о котором говорили ранее.

Если ты дочитал до сюда и все еще легко ставь❤️. В системе есть AIDL интерфейс с названием IConnectivityManager. ConnectivityService это Stub интерфейса IConnectivityManager. Или по другому, к ConnectivityService кто-то может стучаться через Binder и вызывать его методы из другого процесса. И этот кто-то ConnectivityManager, который при создании получает Proxy интерфейса IConnectivityManager. Получив proxy, можно вызывать методы ConnectivityManager, который крутится в ConnectivityServiceInitializer и имеющий доступ к netd, а тем самым к информации о сети.

Жестко, я понимаю сам офигивал от такой картины. Главное тут это то, что во многих аспектах работает связка Manager–Service. Складываем все части мозаики воедино получаем что, есть системные сервисы и есть всякие менеджеры которые мы получаем из Context. Через эти менеджеры мы можем общаться с системными сервисами и общение это происходит через Binder. Разумеется не все что мы получаем через getSystemService обязательно общается с каким-то системным сервисом. Есть например LayoutInflator который ни с кем не общается и просто создается как обычный объект в SystemServiceRegistry.

В этом посте описана суть на примере ConnectivityManager. Есть некоторые сервисы, которые построены сложнее, где по 10 слоев абстракций между менеджером и системным сервисом. Однако основа у всех одна.
59👍9🤯4🔥2
Что такое Zygote
🔥10
Медленно мы подходим к интересным и более хардкорным темам. ZygotеInit каждый видел в стэке вызов функций. Независимо от того, что у вас за приложение стартовать все будет отсюда.

Zygote это термин из биологии, клетка которая возникла при слиянии мужской и женской половых клеток. Zygote после определённого времени начинает делиться клонируя себя. Разработчики назвали этот класс не просто так, потому как в сущности это и происходит каждый раз при старте приложения.

Если кратко погрузится в череду вызов. При старте системы, вот прям с самого начала запускается файл init.rc. Расширение вот такое странное потому как в этом файле используя специальный синтаксис, описывающий инструкции как запускать устройство. Да, разработчики Android сделали систему, которая позволяет при помощи специальных инструкций описать как именно и в каком порядке нужно все запустить.

В init.rc описано как стартовать все демоны, сервисы и остальные элементы системы. Этот же файл запускает AndroidRuntime. В С++ коде он кстати так и называется AndroidRuntime. Этот самый AndroidRuntime в свою очередь запускает JVM и в частности main функцию нашего ZygotеInit.

Что происходит в ZygotInit.main? Для начала подгружаются все классы для работы этой самой JVM и все все Android зависимости необходимые для работы. Если интереснее подробнее разобраться в том, как работают ClassLoader у меня и на это есть серия постов. После загрузки всех необходимых классов, запускается некий ZygoteServer. Этот ZygoteServer запускает бесконечный цикл и чего-то ждет.

Вот тут начинается самое интересное. Эта та магия системного программирования которую практически никогда не увидишь в обычных проектах. Для объяснения нужно написать немного кода на низкоуровневом языке. C++ я жутко не люблю и мне кажется от должен уже отправится на полку истории, поэтому мы черканем пару строк на Rust. Не волнуйтесь сложно не будет, я профессионал. Пример вот такой:

unsafe { // unsafe просто позволяет запускать небезопасные штуки
let fork_result = libc::fork(); //вот тут происходит магия
match fork_result {
-1 => println!("Ошибка при попытке сделать fork"),
0 => println!("Бонжур мы в child процессе, pid {}”, libc::getpid()),
_ => {
println!("Тут мы в изначальном процессе, pid {}", libc::getpid());
}
}
}


Сейчас нужно будет на максимум включить абстрактное мышление. Значит есть в Unix такой системный вызов, который называется fork. Все что он делает, копирует процесс и запускает его как отдельный. Еще раз вдумайтесь! Этот системный вызов берет вашу прогу, копирует все состояния переменных, все потоки, все стэки, весь хип все это дело переносит в отдельное место в оперативной памяти и запускает как отдельный процесс.

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

ZygotInit запускает ZygoteServer, который просто ждет команду от системы на запуск нового процесса. ZygoteServer запускает бесконечный цикл и вот тут интересно. Когда система дает команду на создание нового процесса, этот новый процесс выходит из этого бесконечного цикла и идет дальше запуская ActivityThread и т.д. Изначальный же процесс так и остается в бесконечном цикле в ожидании новых команд от ОС.

Все скорее всего слышали, что когда стартует приложение, у нас создается копия JVM. Мало кто объясняет как именно создается и зачем копия? Как это происходит мы уже разобрали, остался вопрос зачем? Все просто, Android и так не всегда быстрый, а если бы при каждом старте еще и нужно было каждый раз подгружать все нужные классы было бы вообще грустно. Поэтому разработчики придумали тупо копировать инстанс JVM с уже загруженными классами.

Ну а что там происходит с процессом, который вышел из этого бесконечного цикла мы разберем в следующих постах.
👍24🔥62
Минутка мини рекламы.
Мои кореша, тестируют сервис mentee, который помогает повышать скиллы айтишников. Сейчас они проводят эксперимент с форматом тренировочных собеседований, и для этого очень нужен ваш фидбек.

Если хочешь пройти тренировочное собеседование с разработчиками из топовых IT-компаний России (Яндекс, Тинькофф, VK, Авито), а также получить качественную обратную связь – оставь заявку, и с тобой обязательно свяжутся :).

Собеседования проходят в онлайн-режиме, а для лучших кандидатов обещают сделать рефералку в одну из компаний!
Если интересно вот форма 👉 https://forms.gle/Pmj4tLkSiW6zyza57
👍18
Как система дергает методы ЖЦ Activity

👇
🔥15👍3
Итак, я наконец-то добрался до того, чтобы уже сделать предпоследний пост этой серии. Сейчас вы узнаете каким образом дергаются методы ЖЦ все ваших Activity.

Существует куча различных кейсов, как меняются ЖЦ Activity, вот например некоторые из них:
👉 Старт первой Activity
👉 Запуск новой Activity поверх старой
👉 Переворот Activity 
👉 Уничтожение предыдущих Activity из-за нехватки памяти 
👉 Запуск Activity другого приложение поверх наших Activity 

Прикол в том, что для каждого из этих кейсов используются свои сервисы и свои подходы вычисления когда нужно вызывать методы ЖЦ. Если разбирать каждый такой кейс, получится дипломная работа, да и вам эта информация ничего не даст. В посте мы разберем лишь кейс со стартом первой Activity и общий подход как система в целом дергает методы ЖЦ без разбора того, как она понимает когда нужно дернуть методы.

Когда я первый раз увидел код приложения под Android, я был в недоумении, а где же метод main? Все казалось какой-то магией, мы создаем Activity у которых когда-то будут вызваны методы. На самом деле метод main есть и его довольно легко найти. Он находится в классе ActivityThread. ActivityThread это основной поток нашего приложения, тот самый поток в котором работает главный Looper. В ActivityThread сокрыто куча всего, всяких настроек и т.д, но нас сейчас интересует лишь старт Activity.
 
В предыдущем посте мы обсудили, как запускается процесс нашего приложения. Вот что происходит далее, в начале запускается статичный метод main. В этом методе инициализируется главный Looper и после этого дергается метод bindApplication через IPC у ActivityManagerService. Этим мы сообщаем сервису, что вот мы стартанули приложения и нам сейчас нужна помощь с Activity. Далее ActivityManagerService производит свою магию и дергает уже наш метод ActivityThread, т.к при вызове метода bindApplication мы передали объект нашего ActivityTread

После того как ActivityManagerService дернул наш метод, ActivityThread отправляет сообщение BIND_APPLICATION в очередь Looper и запускает этот самый Looper. На этом моменте выполнение метода main прекращается, т.к запустился бесконечный цикл Looper. Далее Looper главного потока выполняет сообщения BIND_APPLICATION которое просто вызывает еще один метод для настройки и дальше ничего не происходит потому как в Looper главного потока больше нет сообщений.

Пока главный поток настраивался, ActivityManagerService подготавливал первую Activity к запуску. Как именно он это делает: на основе информации полученной из ActivityThread ActivityManagerService знает о том, какую Activity нужно запустить. Однако он не создает саму Activity, у него лишь есть информация о том, что это за Activity и как ее нужно запустить. Затем он создает специальный объект, этот объект транзакция, в котором есть вся информация перечисленная выше. Этот объект отправляется через IPC в главный Looper нашего приложения. 

Сообщения транзакции представлены в виде специального сообщения. Как вы знаете можно при помощи специальных колбэков в Handler перехватывать нужные нам сообщения. В нашем приложении есть такой объект как TransactionExecutor, он и перехватывает эти сообщения и затем выполняет.

В переданной транзакции есть информация о том, какой метод ЖЦ какой Activity нужно вызвать. Информация эта представлена специальным объектом, который описывает статус ЖЦ Activity. Если говорить конкретно, то там есть специальный класс наследники которого так и называются OnCreateActivityItem, OnStartActivityItem и т.д. Получив транзакцию TransactionExecutor дергает нужные методы.

По сути это все, в системе есть куча различных сервисов, которые знают о наших Activity и решают у какой Activity какой ЖЦ вызывать. И вызывают они методы ЖЦ через транзакции послание через Binder. Часть транзакции приходят из вне, часть же транзакции создает наше же приложение, например когда одна Activity перекрывает другую. Транзакцию с методом onStop создает сам ActivityThread.
🔥33👍6👏2
Путь от нажатия в лаунчере, до запуска приложения

👇
🔥16
2025/07/10 11:21:26
Back to Top
HTML Embed Code: