3. Основные компоненты - kpb90/start_android GitHub Wiki
Осовные компоненты.
Жизненный цикл приложения.
- Activities
- Content Provider
- Broadcast Receivers
- Services
- Intents -> для взаимодействия компонентов между собой
- Aplications
Activity - окно
- Весь экран или часть
- Может быть запущенно из другого компонента или приложения
- Может возвращать результат
Service - для выполнения фоновых длительных задач
- Не содержит графического интерфейса
- Может выполняться в том же процессе, что и приложение, или в отдельном
- Повышает значимость процесса с точки зрения Android
Broadcast Receiver
- Получает сообщение от Android или других приложений
Пример широковещательных сообщений:
а) BOOT
б) SCREEN_OF/ON
в) CONNECTIVITY_ACTION
- Должен быстро работать -> длительные задачи должен выполнять Service
ContentProvider - компонент для доступа к хранилищу данных
Intent - сущность для описания операции, которую требуется выполнить Используется для запуска:
- Activity
- Service
- Broadcast Receiver
Выполнения стандартных, предоопределенных операции
Жизненный цикл процесса:
Запуск первого компонента
(по умолчанию все компоненты запускаются в рамках одного процесса)
||
\/
ПРОЦЕСС
||
\/
Android убивает процесс.
Если мы хотим запустить компонент Android смотрит есть ли уже процесс для нашего приложения, если существует, то компонент запускается в рамках этого процесса, иначе Android запускает процесс.
Завершение процесса.
Android сам решает когда убить процессы. Для этого он использует важность процессов.
Важность процессов:
- Переднего плана Broadcast Receiver, Activity (с которым пользователь взаимодействует в данных момент времени), Foreground Service
- Видимый - пользователь не взаимодействует, но определенная часть UI видна пользователю.
- Сервисный
- Заднего плана. Пример: Содержит Activity (свернутое приложение)
- Пустой. Не содержит ни одного компонента. Пример: Пользователь запускает приложение -> создается Activity, пользователь нажимает "back", Activity уничтожается, но процесс продолжает висеть (в таком процессе могут хранится закэшированные данные, которые будут использоваться при повторном запуске приложения) -> Такие процессы Android убивает в первую очередь.
Aplication
Создается вместе с процессом. После старта процесса вызывается onCreate Aplication:onCreate -> удобно инициализировать библиотеки. onCreate вызывается в первую очередь до старта Activity, Broadcast Receiver...
OnConfigurationChanged -> вызывается каждый раз, когда меняется конфигурация системы. (переворачивание экрана, подключение хардварной клавы)
Жизненный цикл Activity.
Каждая Activity имеет свой собственный жизненный цикл. Приложение имеет одну или больше Activities плюс процесс Linux привязанный к нему.
Жизненный цикл Activity не привязан к жизненному циклу процесса.
Существует три состояния:
Resumed (возобновленное) – Activity находится на переднем плане и пользователь может взаимодействовать с ней.
Paused (приостановленное) – Activity частично закрыта другой Activity. Activity остается в памяти и сохраняет всю информацию о своем состоянии, а так же остается подключенным к Window Manager).
Stoped (остановленное) – в этом состоянии Activity полностью скрыта, невидима пользователем и находится в фоновом режиме. Остановленная Activity, так же все еще жива, но уже не связана с Window Manager.
Created - в Activity созданы контролы
Started - Activity появилось на экране
Основные методы жизненного цикла Activity:
protected void onCreate() - Android вызывает метод onCreate() после запуска или перезапуска Activity; Внутри этого метода связывают данные с контролами, создают Сервисы и потоки. Задают внешний вид через метод setContentView().
onCreate() принимает Bundle, содержащий данные (для восстановления состояния GUI) сохраненные в onSaveInstanceState. Этот Bundle можно задействовать в
- onRestoreInstanceState()
- внутри onCreate() (Операции по инициализации, занимающие много времени, следует выполнять в фоновом процессе, а не с помощью метода onCreate)
protected void onStart();
protected void onRestart();
protected void onResume();
onResume() можно использовать для инициализации компонентов, которые были освобождены в OnPause(). Пример: для регистрации любых широковещательных приемников или других процессов, которые должны приостанавливаться внутри обработчика onPause().
protected void onPause();
В этом методе необходимо остановить действия, которые загружают процессор. Зафиксировать не сохранённые данные, освободить системные ресурсы, например, обработку данных от GPS. Исходя из архитектуры своего приложения возможно нужно приостановить выполнение потоков, процессов или широковещательных приемников, пока активность не появится на переднем плане. Тем не менее нужно избегать выполнения интенсивной работы в OnPause(), таких как запись в базе данных, лучше подобные операции перенести в onStop()
protected void onStop();
Когда окно становится невидимым для пользователя. В этом методе можно сделать сложные операции по сохранению данных
protected void onDestroy();
Пример жизненного цикла Activity
A::onCreate -> A::onStart -> A::onResume -> A::onPause -> A::onStop -> A::onDestroy
Пример:
A->B->C->A
A->B
A::onPause -> B::onCreate -> B::onStart -> B::onResume -> A::onStop
B->C
B::onPause -> C::onCreate -> C::onStart -> C::onResume -> B::onStop
C->A (была запущена НОВАЯ Активность А)
C::onPause -> A::onCreate -> A::onStart -> A::onResume -> C::onStop
A -> back
A::onPause -> C::onRestart -> C::onStart -> C::onResume -> A::onStop -> A::onDestroy
C -> back -> back
C::onPause -> B::onRestart -> B::onStart -> B::onResume -> C::onStop -> C::onDestroy -> B::onPause ->
-> A::onRestart -> A::onStart -> A::onResume -> B::onStop -> B::onDestroy
###TASK
TASK -> множество Activity, находящиеся в одном backstaсk.
TASK может уйти в Background или выйти в Foreground, когда пользователь к ней возращается.
Каждый TASK имеет свой backstaсk Activity.
Пользователь может переключаться между TASK.
В stack -> может лежать много сущностей одной Activity.
Поведения Activity по умолчанию:
Activity А -> Activity B => Activity А stopped and Android save state (положение прокрутки, текст введенный в поля формы и т.д.).
If user click HOME in Activity B => Activity А resumed.
When user покидает задачу нажав кнопку Home => current Activity stopped and task become in background. Android save state every Activity in task.
If user back to task => task in foreground and Activity on top stack resumed. If user click back, Activity on top stack destroyed, prev Activity resumed.
Activity can has set intstances, and maby in different task.
Можно менять стандартное поведение Activity с помощью Launch Mode.
- standard - при каждом запуске Activity создается новый экземпляр и помещается на вершину стека.
- singleTop - если в момент запуска Activity оно уже есть и находится на вершине стека -> вызывается OnNewIntent
- singleTask
- singleInstance
Запуск Activity в отдельном task
activityIntent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK)
И у Activity указать taskAffinity не совпадающий с именем пакета приложения, иначе будет совпадать.
TaskStackBuilder
Позволяет управлять backstack.
Можно сконструировать стек таска в ручную.
Пример: Мы можем сделать чтобы при нажатии на кнопку back приложение не сворачивалось, а показывалась главное activity;
Все компоненты описываются в Android Manifest
Пересоздание Activity
- Смена ориентации
- При возврате к процессу, который был убит, при этом в backstack остается только верхнее Activity, при нажатии back предыдущее Activity пересоздается.
ConfigChanges
Можно указать при каких параметрах Android не должен переcоздавать Activity => будет вызван onConfigChanged -> где мы можем самостоятельно обработать смену состояний.
Этот параметр стоит использовать в исключительных случаях => полностью от пересоздания Activity мы не можем избавиться.
Пример: При смене ориентации Activity не будет пересоздаваться, но при смене локали будет, так как все ситуации предусмотреть проблематично, лучше не использовать данный параметр.
Сохранение состояния при пересоздании Activity.
a) OnSaveInstanceState()
b) OnRestoreInstanceState
Нет ни какой гарантии что onSaveInstanceState() будет вызвана перед тем как Activity будет уничтожена, поскольку есть случаи в которых сохранение состояния не является необходимым (например, когда пользователь покидает вашу активность используя кнопку ОБРАТНО, то это означает, что пользователь сам прекратил работу с Активностью). Если система вызывает onSaveInstanceState(), то делает это до onStop() и возможно перед onPause().
Перед дестроем в a) передается bundle, в котором мы можем сохранить только то, что может быть сериализовано.
created -> OnRestoreInstanceState
Bundle нужно проверить на NULL. Android использует эти методы для пересоздания, например об editText Android сам позаботится. Но сохраняются не все параметры.
Не всегда данные можно засунуть в bundle. Пример: Объект в котором хранится закэшированный bitmap, чтобы его сохранить потребуется много памяти + будет происходить долго.
Способы сохранения Runtime объектов:
- onRetainNonConfigurationInstance - deprecated
- staticField/singleton/Application object.
singleton.
Если а стеке лежат две разные activity и они используют один и тот же singleton -> может доставлять проблемы:
Объект singleton не уничтожается вместе с activity Пример: Если несколько Activity используют singleton одна из Activity его почистила, а другая Activity этого не ожидала, то это может привести к проблемам.
Сохранение с помощью сервиса.
Особо не отличается от singleton. Отличие -> это асинхронная операция.
RetainInstanceFragment
Google рекомендует использовать данный способ.
Такие фрагменты не пересоздаются в момент пересоздания Activity.
Делаем фрагмент у которого нет UI интерфейса, делаем его Retain и кладем туда модель. После пересоздания Activity мы находим этот фрагмнент, достаем из него модель и подписываем новую Activity на него и продолжаем его использовать.
Intent
(Намерение)
-
запустить другое приложение
-
открыть файл
-
перейти на другой экран
-
явные (explicit) и неявные (implicit)
Intent состоит из
-
Действие - Action (ACTION_VIEW)
-
Категория - category (CATEGORY_LAUNCHER ...)
-
Данные - data
-
Доп. параметры - extras
// this - контекст
// ActivityTwo.class - имя класса
Intent intent = new Intent(this, ActivityTwo.class);
startActivity(intent);
startActivityForResult
Отличие от обычного startActivity в том, что Activity становится «родителем» для запускаемого Activity. И когда запускаемое Activity закрывается, вызывается метод onActivityResult в «родительском» Activity, тем самым давая нам знать, что закрылось Activity, которое мы вызывали методом startActivityForResult.
Параметры onActivityResult:
requestCode – тот же идентификатор, что и в startActivityForResult. По нему определяем, с какого Activity пришел результат.
resultCode – код возврата. Определяет успешно прошел вызов или нет.
data – Intent, в котором возвращаются данные
Схема работы:
Intent - атрибут data. Uri
data - объект над, которым нужно произвести какие-либо действия.
Один из способов присвоения значения этому атрибуту – метод setData (Uri data) у объекта Intent
Uri – это объект, который берет строку, разбирает ее на составляющие и хранит в себе эту информацию. Строка должна быть составлена в соответствии с RFC 2396.
Uri имеет кучу методов, которые позволяют извлекать из распарсенной строки отдельные элементы.
IntentFilter (IF)
Перед запуском Activity с неявным интентом нужно убедиться, что он может быть обработан кем-нить в системе.
Для этого используется: resolveActivity
Для создания явного интента другого приложения удобно использовать: getLaunchForPackage;
Если хотим, чтобы у пользователя был выбор приложения для запуска -> createChooser.
Для запуска сервиса стоит использовать только явные интенты.
В AndroidManifest - указывают на какие запросы они будут реагировать
IF - используются для Activity, Service,BroadCastReceiver
IF - это публичный интерфейс Activity
Если Activity и Service используется только внутри нашего приложения IF не нужен, так как наличие IF делает доступым наши Activity и Service из вне нашего приложения (хотя можно явно указать export = false => доступ будет закрыт).
BroadCast Receiver мы можем зарегать в runtime (без Android manifest)
registerReceiver() unregisterReceiver()
PendingIntent
Позволяет передать интент другому процессу (приложению) для последующего запуска. Например: Действие, которое нужно выполнить, когда user нажимаем на нотификацию в системном трее.