Android app main loop

Android Application Launch explained: from Zygote to your Activity.onCreate()

Apr 30, 2018 · 6 min read

This article explains how Android launches your application when a user clicks an app icon. The Android system does a lot of heavy lifting behind the curtains to make your launch activity visible to a user. This article covers this process in detail by highlighting the important phases and call sequences.

Android applications are unique in two ways:

  1. Multiple Entry points: Android apps are composed of different components and they can invoke the components owned by other apps. These components roughly correspond to multiple entry points for any application. Hence, they differ from traditional applications which have a single entry point like main() method.
  2. Own Little World: Every Android application lives in its own world, it runs in a separate process, it has its own Dalvik VM instance and is assigned a unique user ID.

When does Android process start?

An Android process is started whenever it is required.

Any time a user or some other sys t em component requests a component (could be a service, an activity or an intent receiver) that belongs to your application be executed, the Android system spins off a new process for your app if it’s not already running. Generally processes keep running until killed by the system. Application processes are created on demand and a lot of things happen before you see your application’s launch activity up and running.

Every app runs in its own process: By default, every Android app runs in its own Android process which is nothing but a Linux process which gets one execution thread to start with. For example, when you click on a hyper-link in your e-mail, a web page opens in a browser window. Your mail client and the browser are two separate apps and they run in their two separate individual processes. The click event causes Android platform to launch a new process so that it can instantiate the browser activity in the context of its own process. The same holds good for any other component in an application.

Zygote : Spawning new life, new process

Let’s step back for a moment and have a quick look on system start-up process. Like the most Linux based systems, at startup, the boot loader loads the kernel and starts the init process. The init then spawns the low level Linux processes called “daemons” e.g. android debug daemon, USB daemon etc. These daemons typically handle the low level hardware interfaces including radio interface.

Init process then starts a very interesting process called ‘ Zygote ’.

As the name implies it’s the very beginning for the rest of the Android application. This is the process which initializes a very first instance of Dalvik virtual machine. It also pre-loads all common classes used by Android application framework and various apps installed on a system. It thus prepares itself to be replicated. It stats listening on a socket interface for future requests to spawn off new virtual machines (VM)for managing new application processes. On receiving a new request, it forks itself to create a new process which gets a pre-initialized VM instance.

After zygote, init starts the runtime process.

The zygote then forks to start a well managed process called system server. System server starts all core platform services e.g activity manager service and hardware services in its own context.

At this point the full stack is ready to launch the first app process — Home app which displays the home screen also known as Launcher application.

When a user clicks an app icon in Launcher …

The click event gets translated into startActivity(intent) and it is routed to ActivityManagerService via Binder IPC. The ActvityManagerService performs multiple steps

  1. The first step is to collect information about the target of the intent object. This is done by using resolveIntent() method on PackageManager object. PackageManager.MATCH_DEFAULT_ONLY and PackageManager.GET_SHARED_LIBRARY_FILES flags are used by default.
  2. The target information is saved back into the intent object to avoid re-doing this step.
  3. Next important step is to check if user has enough privileges to invoke the target component of the intent. This is done by calling grantUriPermissionLocked() method.
  4. If user has enough permissions, ActivityManagerService checks if the target activity requires to be launched in a new task. The task creation depends on Intent flags such as FLAG_ACTIVITY_NEW_TASK and other flags such as FLAG_ACTIVITY_CLEAR_TOP.
  5. Now, it’s the time to check if the ProcessRecord already exists for the process.If the ProcessRecord is null, the ActivityManager has to create a new process to instantiate the target component.
Читайте также:  Как разблокировать андроид htc one

As you saw above many things happen behind the scene when a user clicks on an icon and a new application gets launched. Here is the full picture :

There are three distinct phases of process launch :

  1. Process Creation
  2. Binding Application
  3. Launching Activity / Starting Service / Invoking intent receiver …

ActivityManagerService creates a new process by invoking startProcessLocked() method which sends arguments to Zygote process over the socket connection. Zygote forks itself and calls ZygoteInit.main() which then instantiates ActivityThread object and returns a process id of a newly created process.

Every process gets one thread by default. The main thread has a Looper instance to handle messages from a message queue and it calls Looper.loop() in its every iteration of run() method. It’s the job of a Looper to pop off the messages from message queue and invoke the corresponding methods to handle them. ActivityThread then starts the message loop by calling Looper.prepareLoop() and Looper.loop() subsequently.

The following sequence captures the call sequence in detail —

The next step is to attach this newly created process to a specific application. This is done by calling bindApplication() on the thread object. This method sends BIND_APPLICATION message to the message queue. This message is retrieved by the Handler object which then invokes handleMessage() method to trigger the message specific action — handleBindApplication(). This method invokes makeApplication() method which loads app specific classes into memory.

This call sequence is depicted in following figure.

Launching an Activity:

After the previous step, the system contains the process responsible for the application with application classes loaded in process’s private memory. The call sequence to launch an activity is common between a newly created process and an existing process.

The actual process of launching starts in realStartActivity() method which calls sheduleLaunchActivity() on the application thread object. This method sends LAUNCH_ACTIVITY message to the message queue. The message is handled by handleLaunchActivity() method as shown below.

Assuming that user clicks on Video Browser application. the call sequence to launch the activity is as shown in the figure.

The Activity starts its managed lifecycle with onCreate() method call. The activity comes to foreground with onRestart() call and starts interacting with the user with onStart() call.

Thanks for reading through 🙌🏼. If you found this post useful, please applaud using the 👏 button and share it through your circles.

This article was initially published as two part series (Part 1 and Part 2) on my blog ./ mult-core-dump in 2010. These articles have been cited in multiple documents including the very famous Introduction to the Android Graphics Pipeline

Источник

Пишем MVP приложение на Kotlin под Android

Разработка приложений на Kotlin под Android набирает популярность среди разработчиков, однако статей в русскоязычном сегменте Интернета довольно мало. Я решил немного подправить ситуацию, и написать туториал по разработке приложения на Kotlin. Мы напишем полноценное приложение с использованием всех трендовых библиотек (кроме RxJava) в мире Android-разработки. В конце у нас должно получиться расширяемое и легко тестируемое приложение (сами тесты мы писать не будем).

Внимание! В статье описывается создание приложения версии 1.0. Текущий код в репозитории может отличаться от описываемого в статье.

Наверное, некоторые из вас знают, что помимо языка программирования Kotlin JetBrains также разрабатывает библиотеку Anko, для создания UI приложения, в качестве замены обычным XML-файлам. Мы не будем использовать его в нашем проекте, дабы не ставить в затруднительное положение людей не знакомых с Anko.

Настройка Android Studio

Для написания приложений на языке Kotlin, Android Studio нужен специальный плагин. Инструкцию по установке плагина можно найти здесь. Также не забудьте отключить функцию «Instant Run» в настройках Android Studio, т. к. на данный момент она не поддерживается плагином Kotlin.

Для корректной работы генерации кода нужно использовать версию плагина не ниже 1.0.1. Я использовал версию Kotlin 1.0.2 EAP. Вот так выглядит файл build.gradle приложения в моем проекте:

Что будем писать?

Итак, для начала нам нужно определиться что же мы будем писать? Недолго думая я остановился на приложении-заметках. Название тоже придумалось легко — Notelin. Приложение очень простое и состоит из двух экранов:

— Главный экран — содержит в себе список с заметками
— Экран заметки — здесь можно смотреть/редактировать содержание выбранной заметки

Требования к приложению небольшие:

— Добавление/просмотр/удаление заметки
— Просмотр информации о заметке
— Сортировка заметок по заголовку и по дате
— Поиск по заголовкам заметок

Используемые библиотеки

Для работы с базой данных я буду использовать библиотеку Android Active. Урок по работе с ней можно найти по этой ссылке. Для реализации Depency Injection была использована библиотека Dagger 2. На Хабре есть много статей по работе с ней. Основой всего приложения будет библиотека Moxy. С ее помощью мы реализуем паттерн MVP в нашем проекте. Она полностью решает проблемы жизненного цикла, благодаря чему вы можете не переживать о пересоздании компонентов вашего приложения. Также мы воспользуемся набором расширений для языка Kotlin в Android — KAndroid. Про остальные библиотеки я буду рассказывать по ходу дела.

Читайте также:  Android support v4 jar

Ниже приведен список зависимостей проекта:

А вот так выглядит список зависимостей приложения:

Обратите внимание, что вместо apt я использую kapt. Это плагин для Gradle, позволяющий аннотировать Kotlin-элементы.

Структура приложения

Вот так выглядит структура нашего проекта в конечном варианте:

Создаем Model

У заметок будет четыре поля:

  • Дата создания
  • Дата изменения
  • Заголовок
  • Текст

Реализуем все это в коде:

По этой модели библиотекой ActiveAndroid будет создана БД, в которой будут храниться наши заметки. Если вы заметили, у нас есть два конструктора: пустой и с параметрами. Первый конструктор будем использовать мы, а второй — ActiveAndroid. Наша модель наследуется от класса Model, благодаря чему мы можем сохранять и удалять наши заметки просто вызывая методы save() и delete(), например:

Но прежде чем использовать нашу модель, нам нужно прописать кое-какие мета-данные в Manifest-файле:

Думаю, все понятно без комментариев. Осталось унаследовать класс Application от com.activeandroid.app.Application:

Чтобы приложение было менее зависимо от БД я создал обертку NoteDao над нашей моделью, в которой будут происходить все операции по созданию, сохранению, обновлению и удалению заметок:

Наверное, вы заметили, что для создания объектов мы не использовали ключевое слово new — это отличие Kotlin от Java.

Экран с заметками

Также является главным экраном приложения. На нем пользователь может добавить/удалить заметку, просмотреть информацию о заметке, отсортировать их по дате или названию, а также произвести поиск по заголовкам.

Создаем MainView и MainPresenter

Теперь нам нужно перевести все это в код. Для начала создадим интерфейс нашей View:

Далее мы реализуем созданный интерфейс в нашей активити:

Одной из особенностей Kotlin, является то, что наследование и реализация интерфейсов указывается через двоеточие после имени класса. Также не имеет разницы идет название родительского класса перед интерфейсами, после или даже между ними, главное, чтобы класс в списке был один. Т. е. запись выше могла бы выглядеть так:

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

Пока оставим методы пустыми. Как видите, активити наследуется от MvpAppCompatActivity. Это нужно для того, чтобы активити могла восстанавливать состояние при повороте экрана.

Создадим класс презентер:

Презентер также наследуется от MvpPresenter, которому мы указываем с какой View мы будем работать.Осталось инжектировать нашу модель в презентер. Для этого мы создаем модуль — поставщика NoteDao:

Создадим Component для инжектирования презентера:

Теперь нам нужно создать статический экземпляр класса AppComponent в классе Application:

Теперь мы можем инжектировать нашу модель в презентере:

Для взаимодействия MainView и MainPresenter нам нужно создать переменную в MainActivity:

Плагин Moxy сам привяжет View к фрагменту и произведет другие необходимые действия.

Создадим разметку экрана со списком и плавающей кнопкой. Файл activity_main.xml:

Для реализации летающей кнопки я использовал библиотеку FloatingActionButton. Google уже добавили FAB в support-библиотеку, поэтому вы можете воспользоваться их решением.

Укажем нашей Activity, какой макет она должна показывать:

Далее нам нужно связать FAB и список, чтобы при прокручивании списка вверх кнопка исчезала:

Нам не нужно писать порядком надоевший findViewById, нужно лишь прописать одну строчку в блоке с import’ами:

Как видите, последний пакет совпадает с названием нашего xml-файла. IDE автоматически инициализирует свойства (property) наших View и их имена совпадают с ID, которые мы указали в разметке.

Давайте реализуем загрузку заметок из БД. Заметки нужно загружать только один раз и использовать их в последующем. В этом нам поможет метод onFirstViewAttach класса MvpPresenter, который вызывается единожды при первой привязке View к презентеру. Далее, сколько бы мы не крутили и вертели нашу Activity, данные будут закешированы в презентере.

Создадим адаптер для нашего списка:

В адаптере мы используем метод formatDate. Он служит для форматирования даты в строку:

Данный метод находится в файле DateUtils.kt и мы можем использовать как обычный статический метод. Отличие от статического метода здесь в том, что метод принадлежит не классу, а пакету и нам не нужно писать имя класса перед названием метода. В аннотации мы указываем название класса, через который мы будем обращаться к методу из Java. Например в Java дынный метод будет вызываться так:

В методе onNotesLoaded нашей Activity мы показываем наши заметки:

Если заметок нет, то мы показываем сообщение «Нет заметок» в TextView.

Насколько я знаю, для обработки клика по элементам RecycleView не существует «официального» OnItemClickListener. Поэтому мы воспользуемся своим решением:

В методе onCreate нашей Activity пишем:

Функция with позволяет не писать каждый раз имя переменной, а только лишь вызывать методы у объекта, который мы передали в нее. Обратите внимание, что для получения Activity я использовал не просто this, а this@MainActivity. Это связано с тем, что при использовании this в блоке with, возвращается объект, который мы передали в функцию with. При обычном клике по пункту мы переходим на Activity, где мы можем просмотреть текст нашей заметки. При долгом нажатии появляется контекстное меню. Если вы заметили, перед закрывающей скобкой я не написал слово return. Это не ошибка, а особенность языка Kotlin.

Читайте также:  Top android ru роблокс

Вот что происходит при нажатии на пункт меню в презентере:

Мы еще не создали класс NoteActivity, поэтому компилятор будет выдавать ошибку. Для решения этой проблемы можно создать класс NoteActivity или вовсе закомментировать код внутри метода openNote. Запись NoteActivity::class.java аналогична NoteActivity.class в Java. Также заметьте, что мы обращаемся к списку не через метод get(position), а через квадратные скобки, как к обычному массиву.

При использовании MVP-библиотеки Moxy в своем приложении, нам нужно привыкать, что все действия с View, такие как показ/закрытие диалога и другие, должны проходить через презентер. Изначально это не очень привычно и неудобно, но пользы от этого гораздо больше, т. к. мы можем быть уверены, что при пересоздании Activity наше диалоговое окно никуда не пропадет.

Я не буду показывать код контекстного меню, удаления и показа информации о заметке т. к. статья получается очень большой. Но, думаю, общий смысл вы уловили. Также следует отметить, что метод hideNoteContextDialog у презентера должен вызываться даже при закрытии диалога через кнопку назад или при нажатии на область за границами диалога.

При нажатии на FAB должна создаваться новая заметка:

Для создания новой заметки мы вызываем у презентера функция openNewNote:

Метод openNewNote использует созданный нами ранее openNote, в который мы передаем Context и позицию заметки в списке.

Реализуем поиск по заметкам

Давайте добавим поиск по заметкам. Создайте в папке res/menu файл main.xml:

В MainActivity пишем:

При изменении текста в поле поиска мы передаем строку из поля в презентер, после чего показываем результаты в списке. На самом деле, у SearchView нет метода onQueryChange, его добавила библиотека KAndroid.

Реализуем поиск в презентере:

Обратите внимание, как красиво, в одну строчку мы реализовали поиск по списку с помощью метода filter и лямбд. В Java тот же функционал занял бы 6-7строк. Осталось отобразить результаты поиска:

Реализуем сортировку заметок

И последний этап в создании главного экрана, это сортировка заметок. Добавим в res/menu/main.xml следующие строки:

Теперь нам нужно обработать нажатие на пункты меню:

Оператор when является более функциональным аналогом switch-case в Java. Код сортировки в MainPresenter:

Экран с содержанием заметки

Теперь нам нужно создать экран с содержанием заметки. Здесь пользователь может просмотреть/отредактировать заголовок и текст заметки, сохранить или удалить ее, а также просмотреть информацию о заметке.

Создаем NoteView и NotePresenter

Экран содержит всего лишь три View:

-Заголовок
-Дата последнего изменения
-Текст заметки

А вот и сама разметка:

В начале статьи я мельком упомянул об Anko. Библиотека позволяет существенно сократить код, не теряя при этом в удобочитаемости. Вот так, например, выглядела бы наша разметка при использовании Anko:

Но не будем отвлекаться и приступим к написанию кода. Первым делом нам нужно создать View:

Имплементируем NoteView в NoteActivity:

В onCreate мы извлекаем id заметки, чтобы презентер достал заметку из БД и передал данные во View. Создадим презентер:

Не забудьте добавить в класс AppComponent строку:

Покажем нашу заметку:

Реализуем сохранение заметки

Для сохранения заметки нам нужно выбрать соответствующий пункт в меню. Создайте файл res/menu/note.xml:

Опять же, я не стал приводить код удаления и вывода информации о заметке. При просмотре исходного кода, вы можете заметить, что помимо идентификатора заметки я передал в NoteActivity позицию заметки в списке. Это нужно для того, чтобы при удалении заметки на экране просмотра заметки, она также удалялась из списка. Для реализации этого функционала я использовал EventBus. И опять, я не стал приводить код.

На этом все: заметки добавляются, редактируются и удаляются. Также мы можем осуществить поиск и сортировку заметок. Обязательно посмотрите полный исходный код, ссылку на который я привел в конце статьи, чтобы лучше понять как все устроено.

Благодарности

Конечно же, нельзя забывать о людях, которые помогли мне при написании статьи. Хотел бы выразить благодарность хабраюзерам Юрию Шмакову (@senneco) за помощь с его библиотекой Moxy и за помощь по другим вопросам. Также, хочу сказать спаcибо сотруднику JetBrains Роману Белову (@belovrv) за ревью статьи и за предоставленный код на Anko.

UPD: Еще хотел сказать спасибо Sirikid за EPIC COMMIT, благодаря которому я переделал внушительную часть кода с использованием фич Kotlin’а.

Заключение

Надеюсь, эта статья смогла убедить вас в том, что писать приложения на Kotlin не трудно, а может даже и легче, чем на Java. Конечно же, могут встречаться и баги, которые сотрудники JetBrains достаточно быстро фиксят. Если у вас появились какие-либо вопросы, вы можете задать их напрямую разработчикам на Slack-канале. Также вы можете почитать статьи о разработке на Kotlin здесь.

Источник

Оцените статью