- Как Android запускает MainActivity
- Что происходит при запуске приложения
- 1 Схема запуска приложения
- 2. Класс ActivityThread
- 2.1 Подготовка main looper (Process 2–3)
- 2.2 Вызов Handler’a (Process 4-5)
- 2.3 Вызов метод loop() у Looper’а (Process 6–7)
- 3. Бесконечный loop() в Looper’е (Process 7,8,9)
- 4. Запуск MainActivity (Process 10 to 15)
- Русские Блоги
- [Boot] Глава по инициализации запуска системы Android
- I. Обзор
- 1.1 main
- 1.2 система логов
- 1.3 console_init_action
- 1.4 restart_processes
- Два, обработка сигналов
- 2.1 signal_handler_init
- 2.2 reap_any_outstanding_children
- 2.3 register_epoll_handler
- Три, синтаксис файла rc
- 3.1 Action
- 3.2 Service
- 3.3 Command
- 3.4 Options
- В-четвертых, запустите службу
- 4.1 Последовательность запуска
- 4.2 Запуск службы (Zygote)
- 4.3 Перезапуск службы
- Пять, атрибутная служба
- 5.1 property_init
- 5.2 start_property_service
- 5.3 handle_property_set_fd
- 5.4 handle_property_set
- Шесть, резюме
Как Android запускает MainActivity
Недавно я провел исследование о main() методе в Java и то, как он служит точкой входа для любого приложения Java. Это заставило меня задуматься, а как насчет Android-приложений? Есть ли у них основной метод? Как они загружаются? Что происходит за кулисами до выполнения onCreate()? Майкл Бэйли очень подробно рассказал о том, как работает Main Thread, так что это быстрый обзор его доклада плюс дополнительная информация из Android Open Source Project (AOSP).
В этой статье мы рассмотрим:
- Что происходит от нажатия на иконку приложения до запуска MainActivity
- Найдем основной метод приложения и узнаем, как основной поток (он же UI, он же Main Thread) получает свое назначение.
- Рассмотрим роль, которую играют Looper & Handler в передаче сообщений, которые в конечном итоге приводят к созданию вашей Activity.
Что происходит при запуске приложения
1 Схема запуска приложения
Между вызовом метода main() и onCreate() в нашем MainActivity примерно 15 шагов, и в этой статье мы пройдем по ним. На рисунке 1 изображена общая схема запуска приложения, показывающая различные классы взаимодействия сверху и соответствующую цепочку методов. Шаги пронумерованы, и когда я обращаюсь к ним, я буду использовать следующие обозначения Process3 или Process14
Рисунок 1: Схема запуска приложения по шагам от вызова main() до onCreate() в MainActivity
2. Класс ActivityThread
В классе ActivityThread чуть более 6500 строк. Для краткости я определил самые важные для нас части. Давайте рассмотрим, что делает этот класс и связанный с ним основной метод, чтобы запустить нашу Activity
Рисунок 2: Метод main() в ActivityThread, который служит точкой входа для запуска вашего приложения.
Как видно в коде: метод main() выполняет три важных дела:
1. Подготавливает основной Looper (MainLooper) (Process 2)
2. Настройка Handler’a (Process 4)
3. Вызов метода Looper.loop() в главном потоке (MainThread) (Process 6)
2.1 Подготовка main looper (Process 2–3)
Основной Looper задается вызовом Looper.prepareMainLooper() (см. Строку 8 в коде). Это отмечает текущий случайный поток, который выполняет всю работу по вызову метода main() в качестве основного потока приложений. Именно так и именно здесь определяется знаменитый главный поток для приложения в Android!
2.2 Вызов Handler’a (Process 4-5)
Внутри класса ActivityThread существует приватный внутренний класс H, да-да, все верно, просто H, который наследуется от класса Handler (см. рис. 4 и 7). В 12й строке экземпляр H-обработчика устанавливается как главный Handler потока. Что очень интересно знать о классе H, как вы сами увидите позже, это то, что он содержит более 50 определений состояния/событий, в которых может находиться ваше приложение, например LAUNCH_ACTIVITY, PAUSE_ACTIVITY, BIND_SERVICE и т.д.
2.3 Вызов метод loop() у Looper’а (Process 6–7)
После назначения главного потока в этом же главном потоке, для того чтоб мы могли в нем что-то выполнять, вызывается метод Looper.loop() (см. Строку 20). Это начинает выполнение сообщений в очереди сообщений Loopers. Теперь главный поток запущен и может начать обработку задач из очереди.
Обратите внимание, что в строке 18, если выполнение кода пойдет дальше чем Looper.loop() в 17 строке вдруг и приложение выйдет из цикла, то будет брошено исключение RuntimeException. Это говорит о том, что метод loop() в идеале никогда преждевременно не заканчивается. Мы увидим как это в следущем разделе.
3. Бесконечный loop() в Looper’е (Process 7,8,9)
Рисунок 3: Код внутри метода loop() в классе Looper’e
Как мы видим в коде, в методе Looper.loop() есть очередь сообщений (строка 10) и внутри цикла вызывается queue.next(). MessageQueue заполняется Handler-‘ом, о котором мы говорили в предыдущем разделе (см. Process 8). Обратите внимание на интересное описание условия в цикле for — здесь нет аргументов, только две точки с запятой говорят что это бесконечный цикл. Поэтому Looper в идеале никогда не заканчивается, если данное сообщение не null.
Итак, теперь мы определили главный поток, выполняемый благодаря Looper, мы также видели, что Handler добавляет сообщения в цикл Looper.loops() и обрабатывает сообщения. Давайте посмотрим, как они вместе вызывают нашу Activity.
4. Запуск MainActivity (Process 10 to 15)
Важно помнить, что этот бесконечный цикл и обработка сообщений выполнялись в main() методе класса ActivityThread, потому что именно там они были вызваны (см. в коде строки с 12 по 17). Мы поверхностно просмотрели Loopers, MessageQueues и Handlers, чтобы вникнуть в контекст. Итак, давайте вернемся к классу ActivityThread, в частности, к внутреннему классу H, о котором мы говорили ранее, который действует как основной Handler главного потока.
Итак, у нас есть Looper, передающий сообщения нашему Handler’у, давайте узнаем, как эти сообщения обрабатываются. Это делается внутри класса H. Этот класс содержит метод handleMessage(Message msg). Помните, что все классы, которые наследуются от Handler, должны переопределить этот метод.
Рисунок 4: Приватный внутренний класс H и его handleMessage() метод
Как видно в коде, в 8й строке есть оператор switch, в котором определяется обработка входящего сообщения по его содержимому.
Один из случаев (cases) включает в себя запуск активности (строка 11), что интересно, так это то, что этот метод предназначен для обработки около 50 случаев, которые варьируются от возобновления, приостановки, запуска Activity, привязки Service’ов, обработки Receiver’ов, предоставления предупреждений lowMemory или trimMemory, когда память устройства заполняется и т. д.
В case LAUNCH_ACTIVITY вызывается метод handleLaunchActivity(), как показано в строке 13, см Process11 на схеме. Затем этот метод вызывает другой метод, называемый performLaunchActivity(), который возвращает объект Activity (см. Рис. 5, строка 7).
Рисунок 5: Метод handleLaunchActivity() в котором создается Activity
Метод performLaunchActivity() добавляет в Activity важную информацию, такую как Instrumentation, Context, Component, а также Intent; а также задает Application. Затем этот метод вызывает Instrumentation.callActivityOnCreate() (Process 13), который является последним этапом перед вызовом метода onCreate() в Activity (Process 14-15, см. Рисунок 5 (код), строки 8-10).
Рисунок 6: Класс Instrumentation наконец запускает Activity
На данный момент ваша Activity загружена c множеством полезных переменных и методов, которые можно использовать для создания вашего нового удивительного приложения для Android! Все это благодаря ActivityThread, умной работе Handler’a и Looper’a, и огромному классу Activity в 7600 строк кода, который позволяет аттачить фрагменты, получить контекст и легко управлять View’s — и много еще чего.
Источник
Русские Блоги
[Boot] Глава по инициализации запуска системы Android
На основе анализа исходного кода Android 6.0 проанализируйте рабочее содержимое процесса инициализации с номером процесса 1 в процессе запуска Android.
I. Обзор
Процесс init — это первый процесс в пользовательском пространстве в системе Linux, и его номер фиксирован на 1. После запуска ядра процесс init запускается в пользовательском пространстве, и вызывается метод main () в init для выполнения обязанностей процесса init. Функция процесса init разделена на 4 части:
- Разберите и запустите все файлы, связанные с init.rc
- В соответствии с файлом rc сгенерируйте соответствующий узел драйвера устройства
- Обработка завершения дочернего процесса (метод сигнала)
- Обеспечить функцию службы атрибутов
Начнем с метода main ().
1.1 main
После завершения выполнения процесса init он переходит в состояние ожидания цикла epoll_wait.
1.2 система логов
В настоящее время система журналов Android не запущена.Если используется система журналов ядра, открывается узел устройства / dev / kmsg, то его можно передать cat /dev/kmsg Чтобы получить журнал ядра.
Затем установите уровень вывода журнала на KLOG_NOTICE_LEVEL (5). Когда уровень журнала меньше 5, он будет выводиться в журнал ядра. Значение по умолчанию — 3.
1.3 console_init_action
Это экран с ANDROID внизу экрана загрузки.
1.4 restart_processes
Проверьте все службы в service_list. Для служб с флагом SVC_RESTARTING будет вызван соответствующий restart_service_if_needed.
Затем вызовите service_start, чтобы запустить службу.
Затем интерпретируйте четыре основные точки знаний в основном методе инициализации: обработка сигналов, синтаксис файла RC, службы запуска и службы атрибутов.
Два, обработка сигналов
В методе main () файла init.cpp в разделе [1.1] signal_handler_init () используется для инициализации процесса обработки сигнала.
- Инициализировать дескриптор сигнала;
- Циклическая обработка дочерних процессов;
- Зарегистрируйте дескриптор epoll;
- Обработать завершение дочернего процесса;
2.1 signal_handler_init
Каждый процесс должен сначала регистрироваться при обработке сигнальных сигналов, отправленных другими процессами. Когда рабочее состояние процесса изменяется или завершается, генерируется определенный сигнальный сигнал. Процесс init является родительским процессом для всех процессов пользовательского пространства, а когда его дочерний процесс процессы завершаются Когда генерируется сигнал SIGCHLD, процесс init вызывает функцию установки сигнала sigaction (), передает параметры в структуру sigaction и завершает процесс обработки сигнала.
Есть две важные функции: SIGCHLD_handler и handle_signal, а именно:
2.2 reap_any_outstanding_children
Дополнительно: через getprop | grep init.svc Вы можете просмотреть текущее состояние всех служб. Статус делится на: работает, остановлен, перезапуск.
2.3 register_epoll_handler
Когда fd доступен для чтения, он вызовет функцию call (* fn).
Три, синтаксис файла rc
Синтаксис файла rc — это конечная единица строки, синтаксис, разделенный пробелами, начиная с # для представления строк комментариев. Файл rc в основном содержит Action, Service, Command, Options, среди которых имена Action и Service уникальны, а повторяющиеся имена считаются недопустимыми.
3.1 Action
Действие: Определите время выполнения соответствующей службы с помощью триггера, т. Е. Оператора, начинающегося с on. Конкретные сроки следующие:
- on Early-init; Срабатывает на ранней стадии инициализации;
- on init; срабатывает на этапе инициализации;
- on late-init; срабатывает на поздней стадии инициализации;
- при загрузке / зарядном устройстве: срабатывает при запуске / зарядке системы и других ситуациях, которые здесь не перечислены;
- on property: =: запускать, когда значение свойства соответствует условию;
3.2 Service
Служба Служба, начиная с службы, запускается процессом init и обычно выполняется в дочернем процессе init, поэтому перед запуском службы необходимо определить, существует ли соответствующий исполняемый файл. Дочерние процессы, сгенерированные init, определены в файле rc, и каждая служба будет генерировать дочерние процессы с помощью fork при запуске.
Например: service servicemanager /system/bin/servicemanager Он представляет собой имя службы — servicemanager, а путь выполнения службы — / system / bin / servicemanager.
3.3 Command
Вот часто используемые команды
- class_start : запускать все службы, принадлежащие одному классу;
- start : запустить указанную службу, пропустить ее, если она уже запущена;
- stop : остановить работающую службу
- setprop: установить значение свойства
- mkdir
: Создать указанный каталог
: Записать строку в путь к файлу;
3.4 Options
Опции не являются обязательными для Сервиса, используются вместе с Сервисом
- отключено: не запускается автоматически с классом, запускается только в соответствии с именем службы;
- oneshot: служба не перезапускается после выхода;
- пользователь / группа: установите пользователя / группу пользователей, которые запускают службу, по умолчанию — root;
- class: Установите имя класса, к которому он принадлежит. Когда класс, к которому он принадлежит, запускается / завершается, служба также запускается / останавливается. Значение по умолчанию — default;
- onrestart: выполнить соответствующую команду при перезапуске службы;
- сокет: создан с именем /dev/socket/ Разъем
- критично: если служба будет постоянно перезапускаться в течение указанного времени, система перезагрузится и перейдет в режим восстановления.
default: Это означает disabled = false, oneshot = false, critical = false.
В-четвертых, запустите службу
4.1 Последовательность запуска
Последовательность выполнения триггера: Early-init -> init -> late-init. Из приведенного выше кода мы видим, что монтирование файловой системы и при загрузке запускаются в триггере late-init. Процесс загрузки запустит основной класс. Что касается запуска основного класса, то он определяется настройками следующих 4 значений vold.decrypt.Процесс находится в файле system / vold / cryptfs.c.
4.2 Запуск службы (Zygote)
В файле init.zygote.rc служба zygote определяется следующим образом:
от init_parser.cpp После завершения всей работы по анализу сервиса, процесс анализа здесь подробно обсуждаться не будет.Основными задачами процесса являются:
- Создайте служебную структуру под названием «зигота»;
- Создайте структуру socketinfo для связи через сокет;
- Создайте структуру действий, содержащую 4 перезапуска.
Сервис Zygote запустится с запуском основного класса. После выхода zygote будет перезапущен init. Даже если он будет перезапущен несколько раз, он не войдет в режим восстановления. Исполняемый файл, соответствующий zygote, — это / system / bin / app_process, путем вызова pid =fork() Создайте дочерний процесс через execve(svc->args[0], (char**)svc->args, (char**) ENV) , Войдите в функцию main () App_main.cpp. Итак, зигота создается с помощью fork и execv вместе.
Процесс выглядит следующим образом:
Что касается перезапуска Zygote, упомянутого в предыдущем процессе обработки сигналов, он предназначен для обработки сигнала SIGCHLD, а процесс init перезапускает процесс zygote. Для получения дополнительной информации о Zygote см.Зиготные статьи。
4.3 Перезапуск службы
Когда дочерний процесс init завершается, он генерирует сигнал SIGCHLD и отправляет его процессу init, передает данные через сокет сокета, вызывает метод wait_for_one_process () и решает, следует ли перезапустить дочерний процесс или отказаться от запуска в зависимости от того, это один выстрел.
Во всех сервисах есть только servicemanager, zygote и surfaceflinger. onrestart Ключевые слова для запуска процесса запуска других служб.
Это видно из вышеизложенного:
- zygote: инициировать перезапуск медиа, netd и дочерних процессов (включая процесс system_server);
- system_server: запустить перезапуск зиготы;
- Surfaceflinger: запустить зиготу для перезапуска;
- servicemanager: Trigger zygote, healthd, media, surfaceflinger, drm для перезапуска
Следовательно, завершение работы Surfaceflinger, servicemanager, самой зиготы и процесса system_server вызовет перезапуск Zygote.
Пять, атрибутная служба
Когда процесс A изменяет значение свойства с помощью property_set (), процесс инициализации проверяет права доступа. Когда разрешения соответствуют требованиям, соответствующее значение свойства будет изменено. После изменения значения свойства соответствующий триггер (т. Е. Rc file) будет запущен. Оператор в начале on), в Android Shared Memmory (общая область памяти) есть область _system_property_area_, в которой записываются все значения свойств. Для процесса A с помощью метода property_get () также получается значение свойства общей области памяти.
5.1 property_init
Основная функция этого метода — выполнение метода __system_property_area_init () для создания общей памяти для кросс-процессов. Основная работа такова:
- Выполните open (), откройте имя «/ dev /properties«Файл с общей памятью и установите размер 128 КБ;
- Выполните mmap (), чтобы отобразить память процессу инициализации;
- Первый адрес памяти сохраняется в глобальной переменной __system_property_area__, и последующие добавления или изменения свойств основываются на этой переменной для вычисления местоположения.
О загруженном проп-файле
от load_all_load_all_propsprops() Метод, загрузите следующее:
- /system/build.prop;
- /vendor/build.prop;
- /factory/factory.prop;
- /data/local.prop;
- Свойство persist по пути / data / property
5.2 start_property_service
Создайте и контролируйте сокет с именем «property_service», а затем используйте epoll_ctl, чтобы установить дескриптор файла свойств для запуска читаемой функции обратного вызова handle_property_set_fd, а затем посмотрите на реализацию этой функции.
5.3 handle_property_set_fd
Здесь 2-секундный тайм-аут установлен для события приема сокета, что означает, что процесс установки свойства может занять много времени.
5.4 handle_property_set
Здесь он проверит, соответствует ли имя атрибута. Конкретные спецификации проверки следующие:
Логика выполнения разных атрибутов разная, основные отличия заключаются в следующем:
Имя атрибута начинается с
Вначале это означает, что это управляющее сообщение, которое используется для выполнения некоторых команд. Например:
- setprop ctl.start bootanim просмотр анимации загрузки;
- setprop ctl.stop bootanim Отключить загрузочную анимацию;
- setprop ctl.start pre-recovey переходит в режим восстановления;
Имя атрибута начинается с ro. Вначале это означает, что он доступен только для чтения и не может быть установлен, поэтому он возвращается напрямую;
Имя атрибута начинается с persist. Вначале вам нужно записать эти значения в соответствующий файл; следует отметить, что persist используется для сохранения определенных значений атрибутов, но также приносит дополнительные операции ввода-вывода.
Шесть, резюме
Процесс init (pid = 1) — это первый процесс в пространстве пользователя в системе Linux, и его основные задачи следующие:
- Создайте общую память для сервера атрибутов;
- Проанализируйте каждый RC-файл и запустите соответствующий процесс службы атрибутов;
- Инициализируйте epoll, установите соответствующие функции обратного вызова signal, property и keychord, когда три fd доступны для чтения;
- Войдите в состояние бесконечного цикла и выполните следующий процесс:
- Проверить, пуст ли список action_queue, если он не пустой, выполнить соответствующее действие;
- Проверьте, нужно ли перезапускать процесс, и перезапустите его, если он есть;
- Войдите в состояние ожидания epoll_wait до тех пор, пока событие изменения системного свойства (свойство_set не изменит значение свойства), или сигнал SIGCHLD от дочернего процесса не будет получен, или событие ввода с клавиатуры keychord, он выйдет из состояния ожидания и выполнит соответствующую функцию обратного вызова.
Можно видеть, что основная работа процесса инициализации после загрузки заключается в том, чтобы реагировать на события изменения свойств и восстанавливать зомби-процесс. Когда процесс вызывает свойство_set для изменения значения системного свойства, система отправляет уведомление о событии изменения свойства процессу init через сокет, затем свойство fd станет доступным для чтения, а процесс init будет использовать механизм epoll для мониторинга fd. и вызвать метод обратного вызова handle_property_set_fd (). Повторное использование зомби-процессов в ядре Linux, если родительский процесс завершается, не дожидаясь окончания дочернего процесса, дочерний процесс после завершения станет зомби-процессом, занимая системные ресурсы. По этой причине процесс init специально устанавливается с приемником сигнала SIGCHLD. Когда некоторые дочерние процессы завершаются и обнаруживают, что их родительский процесс завершился, он отправляет сигнал SIGCHLD процессу init, и процесс init вызывает метод обратного вызова handle_signal (), чтобы вернуть дочерний процесс зомби.
Источник