- «Холодный» запуск Android-приложения
- Общая схема
- Открывая «окно»…
- «Разделение» Zygote-ы
- Приложение запустилось!
- Контроль над приложением
- Эпилог
- Отрисовка первого кадра Android-приложения
- Старт главного потока
- Планируем запуск Activity
- Фактический запуск Activity
- Первый кадр
- Заключение
- Why app crash after building to Android? #393
- Comments
- aidanikuz02 commented Feb 19, 2020
- aidanikuz02 commented Feb 19, 2020
- aidanikuz02 commented Feb 19, 2020
- aidanikuz02 commented Feb 19, 2020
«Холодный» запуск Android-приложения
Всем приветъ! Давно ничего не писал.
Это будет серия постов о процессе «холодного» запуска Android приложения, с момента нажатия на иконку и до создания процесса приложения.
Общая схема
Открывая «окно»…
Перед тем как запустить новый процесс приложения, system_server создает стартовое окно используя метод PhoneWindowManager.addSplashScreen():
Стартовое окно это то, что пользователь будет видеть пока запускается само приложение. Окно будет отображаться до тех пор пока не будет запущена Activity и не будет отрисован первый кадр. То есть пока не будет завершен «холодный» запуск. Пользователь может видеть данное окно длительное время, поэтому постарайтесь сделать его приятным.
Содержимое стартового окна берется из drawable-ресурсов windowSplashscreenContent и windowBackground запускаемого Activity. Банальный пример такого окна:
Если пользователь восстанавливает Activity из режима последнего экрана(Recent screen), при этом на нажимая на иконку приложения, то system_server вызывает метод TaskSnapshotSurface.create(), чтобы создать стартовое окно из уже сделанного скриншота.
Как только стартовое окно показано пользователю, system_server готов запустить процесс приложения и вызывает метод ZygoteProcess.startViaZygote():
В коде видно, что метод ZygoteProcess.zygoteSendArgsAndGetResult() отправляет аргументы запуска через сокет Zygote-процессу.
«Разделение» Zygote-ы
Каждый процесс приложения запускается с помощью форкания(разделения) от существующего Zygote-процесса…
Вкратце об этом я писал в предыдущей статье про запуск Android-а. А теперь давайте посмотрим поглубже на происходящие процессы.
Когда система загружается процесс Zygote стартует и выполняет метод ZygoteInit.main():
Как вы видите метод ZygoteInit.main() делает 2 важные вещи:
- Подгружает все необходимые системные библиотеки и ресурсы Android-фреймворка. Подобная предзагрузка не только экономит память но еще и экономит время запуска приложений.
- Далее он запускает метод ZygoteServer.runSelectLoop(), который в свою очередь запускает сокет и начинает слушать вызовы данного сокета.
Когда же на сокет приходит команда на форкинг процесса, метод ZygoteConnection.
processOneCommand() обрабатывает аргументы используя метод ZygoteArguments.parseArgs() и запускает метод Zygote.forkAndSpecialize():
На заметку: Начиная с Android 10 есть оптимизационная фича под названием Unspecialized App Process, которая имеет пул не специализированных Zygote-процессов, для еще более быстрого запуска приложений.
Приложение запустилось!
После форка дочерний процесс запускает метод RuntimeInit.commonInit(), который устанавливает дефолтный UncaughtExceptionHandler. Далее, процесс запускает метод ActivityThread.main():
Тут происходят две интересные вещи:
- Метод ActivityThread.main() создает новый поток(Thread) и вызывает метод Looper.loop(), в котором будет запущен новый инстанс Looper-а. Он будет привязан к новому потоку(который становится MainThread-ом aka UiThread) и будет работать(теоретически) бесконечно. Looper привязавшись, будет ожидать сообщений для того чтобы поместить их к своему MessageQueue.
- Далее, метод ActivityThread.attach() делает IPC-запрос к методу ActivityManagerService.attachApplication()system_server-а, тем самым давая понять, что MainThread нашего приложения запущен и готов к работе.
Контроль над приложением
В процессе system_server метод ActivityManagerService.attachApplication() вызывает метод ActivityManagerService.attachApplicationLocked(), который завершает настройку запускаемого приложения:
Парочка ключевых выводов:
- Процесс system_server делает IPC-запрос к методу ActivityThread.bindApplication() в процессе нашего приложения, который направляет запрос к методу ActivityThread.handleBindApplication() в MainThread-е приложения.
- Сразу после этого, system_server планирует запуск Pending Activity, Service и BroadcastReciever-ов нашего приложения.
- Метод ActivityThread.handleBindApplication() загружает APK-файл и компоненты приложения.
- Разработчики имеют возможность немного повлиять на процессы перед запуском метода ActivityThread.handleBindApplication(), так что именно здесь должен начаться мониторинг холодного запуска приложения.
Давайте немного подробно разберем 3-ий пункт и узнаем что и как происходит при загрузке компонентов и ресурсов приложения. Порядок шагов такой:
- Загрузка и создание инстанса класса AppComponentFactory.
- Вызов метода AppComponentFactory.instantiateClassLoader().
- Вызов метода AppComponentFactory.instantiateApplication() для загрузки и создания инстанса класса Application.
- Для каждого объявленного ContentProvider-а, в порядке приоритета, вызов метода AppComponentFactory.instantiateProvider() для загрузки его класса и создания инстанса, после вызов метода ContentProvider.onCreate().
- И наконец, вызов метода Application.onCreate().
Эпилог
Мы начали изучать «холодную» загрузку с очень обще-абстрагированного уровня:
Теперь мы знаем, что происходит «под капотом»:
Ну что же, это был длинный пост. Но это не все! В следующих постах мы продолжим глубокое погружение в процесс запуска Android-приложения. Оставайтесь с нами!
Источник
Отрисовка первого кадра Android-приложения
Всем приветЪ! Этот пост является продолжением поста про глубокое погружение в процесс загрузки-запуска Android-приложения. Сегодня мы пойдем чуть дальше и обсудим момент когда главная Activity приложения запущена и система должна отрисовать первый кадр. Прошу под кат.
Следуя официальной документации запущенный процесс приложения отвечает за выполнение следующих шагов:
- Создание объекта класса Application.
- Запуск основного потока(MainThread aka UiThread).
- Создание стартового Activity, который указан в манифесте.
- Расширение(раздутие, inflating) вьюшек. То есть создание вьюшек, которые прописаны в xml-файле.
- Планировка размеров(View.measure()) и размещения(View.layout()) вьюшек на экране.
- Выполнение начальной отрисовки.
После того как был отрисован первый кадр, системный процесс заменяет отображаемое фоновое окно, заменяя его на Activity приложения. Теперь пользователь может взаимодействовать с приложением.
А теперь давайте поподробнее обо всех шагах.
Старт главного потока
В предыдущем посте мы узнали:
- Когда запускается процесс приложения, он вызывает метод ActivityThread.main(), который делает блокирующий IPC-запрос к методу ActivityManagerService.attachApplication() в процессе system_server.
- system_server делает IPC-вызов в процессе приложения метода ActivityThread.bindApplication(), который ставит в очередь сообщение BIND_APPLICATION в MessageQueue главного потока.
- Когда IPC-вызов метода ActivityManagerService.attachApplication() завершен, ActivityThread.main() вызывает Looper.loop(), который будет зациклен навсегда(пока приложение работает) и будет обрабатывать сообщения поступающие в MessageQueue.
- Первое сообщение, которое будет обработано это BIND_APPLICATION. В этот момент будет вызван метод ActivityThread.handleBindApplication(), который загрузит APK и другие компоненты приложения.
Важный момент: ничего не происходит в главном потоке процесса приложения пока не выполнится IPC-вызов метода ActivityManagerService.attachApplication().
Планируем запуск Activity
Давайте посмотрим что происходит в процессе system_server после вызова метода ActivityThread.bindApplication():
Строка которая релевантна запуску Activity — mAtmInternal.attachApplication(. ). Метод вызывает ActivityTaskManagerService.attachApplication(), который в свою очередь вызывает метод RootActivityContainer.attachApplication():
Код делает следующее:
- Обходит каждый дисплей.
- Получает стек сфокусированных Activity для этого дисплея.
- Проходит по каждому Activity целевого стека Activity.
- Если Activity принадлежит к запущенному процессу, то вызывается метод ActivityStackSupervisor.realStartActivityLocked(). Обратите внимание, что параметр andResume будет иметь значение true если Activity находится на вершине стэка.
Вот как выглядит метод ActivityStackSupervisor.realStartActivityLocked():
Все вызовы методов, которые мы просмотрели происходят в системном процессе system_server. Метод ClientLifecycleManager.scheduleTransaction() делает IPC-вызов ActivityThread.scheduleTransaction() в процессе приложения, который вызывает ClientTransactionHandler.scheduleTransaction(), чтобы положить в очередь сообщение EXECUTE_TRANSACTION:
При обработке сообщения EXECUTE_TRANSACTION происходит вызов метода TransactionExecutor.execute().
Теперь можно обновить диаграмму:
Фактический запуск Activity
Метод TransactionExecutor.execute() вызывает TransactionExecutor.
performLifecycleSequence(), который в свою очередь делает коллбэк в ActivityThread для создания(create), запуска(start) и продолжения(resume) Activity:
Первый кадр
Давайте взглянем на последовательность вызовов методов, которые ведут к отрисовке первого кадра:
- ActivityThread.handleResumeActivity()
- WindowManagerImpl.addView()
- WindowManagerGlobal.addView()
- ViewRootImpl.setView()
- ViewRootImpl.requestLayout()
- ViewRootImpl.scheduleTraversals()
- Choreographer.postCallback()
- Choreographer.scheduleFrameLocked()
Метод Choreographer.scheduleFrameLocked() ставит в очередь сообщение MSG_DO_FRAME:
При обработке сообщения MSG_DO_FRAME происходит вызов метода Choreographer.doFrame(), который в свою очередь вызывает ViewRootImpl.doTraversal(), который осуществляет проходы measure pass и layout pass, и наконец проход the first draw pass по иерархии вьюшек:
Заключение
Мы начали с высокого уровня понимания того, что происходит, когда система создает процесс приложения:
Теперь мы знаем что именно происходит «под капотом»:
А теперь давайте соединим диаграммы из предыдущего поста, с того момента когда пользователь тапает на иконку приложения до момента отрисовки первого кадра:
Теперь, когда у нас есть полная картина, мы можем начать разбираться в том, как правильно контролировать холодный запуск. Следующий пост будет именно об этом! До встречи.
Источник
Why app crash after building to Android? #393
Comments
aidanikuz02 commented Feb 19, 2020
Hello. I am using Unity 2019.3.0f3, Windows10, version 7.18.0.
I created a project that successfully built and run. Then i exported it as a package and imported it into another project that uses EasyAR camera function. I created a new app in the console so i got a new ID and entered the package name as per the new project. Updated the app id in unity facebook settings like attached images 3 and 4. but when building to my Samsung s10 it crashes with errors : Didnt get provider and didnt find class com.facebook.FacebookContentProvider like attached images 1 and 2. The AndroidManifest is as image 5. the generated hash key for android i did like this from the previous project, so its still the same. Please help.
The text was updated successfully, but these errors were encountered:
aidanikuz02 commented Feb 19, 2020
I also added -keep class com.facebook.internal.* <*;>and -keep class com.facebook.* <*;>in proguard-user.txt file (as suggested in this closed issue ) which was created when i checkmarked user proguard file at Player Settings > Publishing Settings > Build : User Proguard File.
But i still got the same error as above.
aidanikuz02 commented Feb 19, 2020
run «/Assets/Play Service Resolver/Android Resolver/Delete Resolved Libraries»
run «/Assets/Play Service Resolver/Android Resolver/Resolve»
Still got the error
aidanikuz02 commented Feb 19, 2020
Okay, so what i did was delete the PlayServicesResolver in Assets. Then I imported play-services-resolver-1.2.135.0.unitypackage which can be downloaded here : https://github.com/googlesamples/unity-jar-resolver
then Assets > Play Services Proivider > Android Resolver > Resolve
Источник