Основы работы с приложениями

Приложения для Android можно писать на языках программирования Kotlin, Java и C++. Инструменты Android SDK компилируют код, данные и файлы ресурсов в APK-файл или набор Android App Bundle.

Пакет Android – это архивный файл с суффиксом .apk, который содержит контент приложения Android, необходимый для его работы. Именно этот файл используется для установки приложения на устройствах Android.

Набор Android App Bundle – это архивный файл с суффиксом .aab, который содержит все компоненты проекта приложения Android, включая дополнительные метаданные, не требующиеся во время выполнения. AAB – это формат публикации, который нельзя установить на устройства Android. Создание и подписание APK-файлов происходит на более позднем этапе.

Например, при распространении приложения через Google Play серверы этого сервиса генерируют оптимизированные APK-файлы, которые содержат только ресурсы и код, необходимые для конкретного устройства, запрашивающего установку приложения.

Каждое приложение Android работает в собственной изолированной среде, защищенной следующими функциями безопасности Android:

  • Операционная система Android – это многопользовательская система Linux, в которой каждое приложение является отдельным пользователем.
  • По умолчанию система назначает каждому приложению уникальный идентификатор пользователя Linux, который используется только системой и неизвестен приложению. Система устанавливает разрешения для всех файлов в приложении, чтобы доступ к ним мог получить только идентификатор пользователя, назначенный этому приложению.
  • У каждого процесса есть собственная виртуальная машина, поэтому код приложения выполняется изолированно от других приложений.
  • По умолчанию каждое приложение выполняется в собственном процессе Linux. Система Android запускает процесс, когда требуется выполнить любой из компонентов приложения, и завершает его, когда он больше не нужен или когда системе необходимо освободить память для других приложений.

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

Однако приложение может обмениваться данными с другими приложениями и получать доступ к системным сервисам.

  • Можно настроить два приложения так, чтобы они использовали один и тот же идентификатор пользователя Linux. В этом случае они смогут получать доступ к файлам друг друга. Чтобы экономить системные ресурсы, приложения с одинаковым идентификатором пользователя могут работать в одном процессе Linux и использовать одну виртуальную машину. Приложения также должны быть подписаны одним сертификатом.
  • Приложение может запрашивать разрешение на доступ к данным устройства, например к местоположению, камере и Bluetooth-подключению. Пользователь должен явным образом предоставить эти разрешения. Подробнее о разрешениях в Android…

В этом документе рассматриваются следующие понятия:

  • Основные компоненты фреймворка, определяющие ваше приложение.
  • Файл манифеста, в котором вы объявляете компоненты и необходимые функции устройства для вашего приложения.
  • Ресурсы, которые не входят в код приложения и позволяют ему оптимизировать свою работу для различных конфигураций устройств.

Компоненты приложения

Компоненты приложения – это основные строительные блоки приложения Android. Каждый компонент представляет собой точку входа, через которую система или пользователь может войти в ваше приложение. Некоторые компоненты зависят от других.

Существует четыре типа компонентов приложения:

  • Объекты activity
  • Услуги
  • Широковещательные приемники
  • Поставщики контента

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

Активность
Операция – это точка входа для взаимодействия с пользователем. Оно представляет собой один экран с пользовательским интерфейсом. Например, в почтовом приложении может быть одно действие, которое показывает список новых писем, другое действие для составления письма и ещё одно действие для чтения писем. Хотя действия работают вместе, чтобы сформировать целостный пользовательский опыт в почтовом приложении, каждое из них независимо от других.

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

Действие обеспечивает следующие ключевые взаимодействия между системой и приложением:

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

Вы реализуете действие как подкласс класса Activity. Подробнее о классе Activity рассказывается в статье Введение в действия.

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

Например, сервис может воспроизводить музыку в фоновом режиме, пока пользователь находится в другом приложении, или получать данные по сети, не блокируя взаимодействие пользователя с действием. Другой компонент, например действие, может запустить службу и позволить ей работать или привязаться к ней для взаимодействия.

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

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

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

Привязанные службы запускаются, когда другое приложение или система запрашивает их использование. Привязанная служба предоставляет API другому процессу, и система знает, что между этими процессами есть зависимость. Если процесс A привязан к сервису в процессе B, система знает, что ей нужно поддерживать работу процесса B и его сервиса для процесса A. Кроме того, если процесс А важен для пользователя, то и процесс Б также будет считаться важным.

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

Сервис реализуется как подкласс Service. Подробную информацию о классе Service можно найти в обзоре сервисов.

Примечание. Если ваше приложение предназначено для Android 5.0 (уровень API 21) или более поздней версии, используйте для планирования действий класс JobScheduler. JobScheduler позволяет экономить заряд батареи, оптимально планируя задания и работая с API спящего режима. Дополнительную информацию об использовании этого класса можно найти в JobScheduler справочной документации.

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

Например, приложение может запланировать будильник, чтобы отправить уведомление о предстоящем событии. Поскольку будильник доставляется в BroadcastReceiver в приложении, ему не нужно оставаться запущенным до срабатывания будильника.

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

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

Например, широковещательный приемник может запланировать выполнение задачи JobService на основе события, используя JobScheduler. Широковещательные приемники часто используются для взаимодействия приложений друг с другом, поэтому при их настройке важно учитывать последствия для безопасности.

Широковещательный приемник реализуется как подкласс BroadcastReceiver, а каждая трансляция доставляется как объект Intent. Дополнительную информацию вы найдете в описании класса BroadcastReceiver.

Поставщики контента
Поставщик контента управляет общим набором данных приложения, которые можно хранить в файловой системе, базе данных SQLite, интернете или любом другом постоянном хранилище, доступном для приложения. Через поставщика контента другие приложения могут запрашивать или изменять данные, если это разрешено.

Например, в системе Android есть поставщик контента, который управляет контактной информацией пользователя. Любое приложение с соответствующими разрешениями может запрашивать у поставщика контента, например с помощью ContactsContract.Data, информацию об определенном человеке, а также записывать ее.

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

Для системы поставщик контента – это точка входа в приложение для публикации именованных элементов данных, идентифицируемых по схеме URI. Таким образом, приложение может решить, как оно хочет сопоставить содержащиеся в нем данные с пространством имен URI, передавая эти URI другим объектам, которые, в свою очередь, могут использовать их для доступа к данным. Это позволяет системе выполнять следующие действия:

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

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

Поставщик контента реализуется как подкласс ContentProvider и должен реализовывать стандартный набор API, позволяющий другим приложениям выполнять транзакции. Дополнительную информацию см. в руководстве для разработчиков по поставщикам контента.

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

Когда система запускает компонент, она запускает процесс для этого приложения, если он ещё не запущен, и создает экземпляры классов, необходимых для компонента. Например, если ваше приложение запускает действие в приложении камеры, которое делает снимок, это действие выполняется в процессе, принадлежащем приложению камеры, а не в процессе вашего приложения. Поэтому, в отличие от приложений для большинства других систем, у приложений для Android нет единой точки входа, то есть функции main().

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

Активировать компоненты

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

Намерение создается с помощью объекта Intent, который определяет сообщение для активации определенного компонента (явное намерение) или определенного типа компонента (неявное намерение).

Для действий и сервисов намерение определяет действие, которое необходимо выполнить, например просмотр или отправка чего-либо, и может указывать URI данных, с которыми необходимо выполнить действие, а также другие сведения, которые могут потребоваться запускаемому компоненту.

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

Для широковещательных приемников намерение определяет широковещательное объявление. Например, широковещательное сообщение о низком заряде батареи содержит только строку действия battery is low.

В отличие от действий, сервисов и широковещательных приемников, поставщики контента активируются при получении запроса от ContentResolver. Он обрабатывает все прямые транзакции с поставщиком контента, а компонент, выполняющий транзакции с поставщиком, вызывает методы объекта ContentResolver. Это обеспечивает уровень абстракции между поставщиком контента и компонентом, запрашивающим информацию, что необходимо для безопасности.

Для каждого типа компонентов предусмотрен свой способ активации:

  • Вы можете запустить действие или дать ему новую задачу, передав Intent в startActivity() или, если вы хотите, чтобы действие вернуло результат, startActivityForResult().
  • На устройствах с Android 5.0 (API уровня 21) и более поздними версиями для планирования действий можно использовать класс JobScheduler. В более ранних версиях Android вы можете запустить службу или передать новые инструкции работающей службе, передав Intent в startService(). Вы можете подключиться к сервису, передав Intent в bindService().
  • Вы можете инициировать трансляцию, передав Intent в такие методы, как sendBroadcast() или sendOrderedBroadcast().
  • Чтобы выполнить запрос к поставщику контента, вызовите метод query() для объекта ContentResolver.

Дополнительную информацию об использовании интентов можно найти в статье Интенты и фильтры интентов. Дополнительную информацию об активации отдельных компонентов можно найти в следующих документах: Введение в действия, Обзор сервисов, BroadcastReceiver и Поставщики контента.

Файл манифеста

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

Помимо объявления компонентов приложения, манифест выполняет ряд других функций, например:

  • Определяет разрешения пользователя, которые требуются приложению, например доступ к интернету или доступ для чтения к контактам пользователя.
  • Объявляет минимальный уровень API, необходимый для работы приложения, в зависимости от того, какие API оно использует.
  • Объявляет аппаратные и программные функции, используемые или требуемые приложением, такие как камера, службы Bluetooth или мультисенсорный экран.
  • Объявляет библиотеки API, с которыми должно быть связано приложение (кроме API платформы Android), например библиотеку Google Карт.

Объявление компонентов

Основная задача манифеста – сообщить системе о компонентах приложения. Например, в файле манифеста можно объявить объект activity следующим образом:

<?xml version="1.0" encodin>g<="utf-8&>quot;<?
manifest ... 
    application android:icon="@dr>awable/ap<p_icon.png" ... 
        activity android:name="com.example.project.ExampleActivity"
                  an>droid:lab<el=">@string/example_l<abel" .>.<. 
      >  /activity
        ...
    /application
/manifest

В элементе <application> атрибут android:icon указывает на ресурсы для значка, который идентифицирует приложение.

В элементе <activity> атрибут android:name указывает полное имя класса подкласса Activity, а атрибут android:label – строку, которая будет использоваться в качестве видимого пользователю ярлыка для действия.

Все компоненты приложения должны быть объявлены с помощью следующих элементов:

  • <activity> элементов для действий
  • Элементы <service> для услуг
  • Элементы <receiver> для широковещательных приемников
  • Элементы <provider> для поставщиков контента

Действия, сервисы и поставщики контента, которые вы включаете в исходный код, но не объявляете в манифесте, не видны системе и, следовательно, не могут быть запущены. Однако широковещательные приемники можно объявить в манифесте или динамически создать в коде как объекты BroadcastReceiver и зарегистрировать в системе, вызвав registerReceiver().

Подробнее о структуре файла манифеста приложения…

Объявление возможностей компонента

Как описано в разделе Активация компонентов, вы можете использовать Intent для запуска действий, сервисов и широковещательных приемников. Для этого в объекте intent нужно явно указать целевой компонент, используя название класса компонента. Вы также можете использовать неявное намерение, которое описывает тип выполняемого действия и, при необходимости, данные, над которыми вы хотите выполнить действие. Неявное намерение позволяет системе найти на устройстве компонент, который может выполнить действие, и запустить его. Если действие, описанное в намерении, могут выполнить несколько компонентов, пользователь выбирает, какой из них использовать.

Внимание! Если вы используете намерение для запуска Service, убедитесь, что ваше приложение защищено, используя явное намерение. Использование неявного намерения для запуска сервиса представляет угрозу безопасности, поскольку вы не можете быть уверены, какой сервис ответит на намерение, а пользователь не может видеть, какой сервис запускается. Начиная с Android 5.0 (уровень API 21), система выдает исключение, если вы вызываете bindService() с неявным намерением. Не объявляйте фильтры намерений для своих сервисов.

Система определяет компоненты, которые могут ответить на интент, сравнивая полученный интент с фильтрами интентов, указанными в файле манифеста других приложений на устройстве.

При объявлении Activity в файле манифеста приложения можно включить фильтры интентов, которые объявляют возможности Activity, чтобы оно могло реагировать на интенты из других приложений. Для этого добавьте элемент <intent-filter> в качестве дочернего элемента объявления компонента.

Например, если вы создаете почтовое приложение с действием для создания нового письма, вы можете объявить фильтр интентов, чтобы отвечать на интенты "отправить" для отправки нового письма, как показано в следующем примере:

<manifest ... >
    ...
 <   application .>.. 
     <   activity android:name="com.example.project.ComposeEmailActivity"&<gt;
            intent-filter&g<t;
                action android:name="android.intent.action.S<END" />
                data androi<d:type="*/*" />
                category android:name=&quo<t;android.intent.categor<y.DEFAULT"< />
     > <      /intent-filter>
        /activity>
    /application
/manifest>

Если другое приложение создает намерение с действием ACTION_SEND и передает его в startActivity(), система может запустить ваше действие, чтобы пользователь мог составить и отправить электронное письмо.

Подробнее о создании фильтров интентов…

Укажите требования к приложению

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

Большинство этих деклараций носят исключительно информационный характер. Система не считывает их, но внешние сервисы, такие как Google Play, используют их для фильтрации приложений, когда пользователи ищут их на своих устройствах.

Например, для работы вашего приложения требуется камера и используются API, представленные в Android 8.0 (уровень API 26). Вы должны задекларировать эти требования. Значения minSdkVersion и targetSdkVersion задаются в файле build.gradle модуля приложения:

android {
  ...
  defaultConfig {
    ...
    minSdkVersion 26
    targetSdkVersion 29
  }
}

Примечание. Не задавайте minSdkVersion и targetSdkVersion непосредственно в файле манифеста, поскольку они будут перезаписаны Gradle в процессе сборки. Подробнее о том, как указать требования к уровню API…

Вы объявляете функцию камеры в файле манифеста приложения:

<manifest ... >
 <   uses-feature android:name="android.hardware.camera.any"
                  android:required="<;true" />
    ...
/manifest>

Если вы добавите в манифест строки из примеров выше, то пользователи устройств без камеры или с версией Android ниже 8.0 не смогут установить ваше приложение из Google Play. Однако вы также можете указать, что приложение использует камеру, но она не является обязательной. Для этого задайте атрибуту required значение false, проверяйте во время выполнения, есть ли на устройстве камера, и при необходимости отключайте функции, связанные с ней.

Подробнее о том, как управлять совместимостью приложения с разными устройствами…

Ресурсы приложения

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

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

Для каждого ресурса, который вы включаете в проект Android, инструменты сборки SDK определяют уникальный целочисленный идентификатор, который можно использовать для ссылки на ресурс из кода приложения или из других ресурсов, определенных в XML. Например, если в приложении есть графический файл с названием logo.png (сохраненный в каталоге res/drawable/), SDK Tools создадут идентификатор ресурса с названием R.drawable.logo. Этот идентификатор сопоставляется с целым числом, которое относится к определенному приложению и позволяет ссылаться на изображение и вставлять его в пользовательский интерфейс.

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

Например, если вы определите строки интерфейса в XML, то сможете перевести их на другие языки и сохранить в отдельных файлах. Затем Android применяет к интерфейсу строки на нужном языке в зависимости от квалификатора языка, который вы добавляете к названию каталога ресурсов (например, res/values-fr/ для строк на французском языке), и языковых настроек пользователя.

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

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

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

Дополнительные ресурсы

Чтобы научиться разрабатывать приложения для Android, посмотрите видео и изучите руководства по программированию в курсе Udacity Разработка приложений для Android на языке Kotlin.

Дополнительная информация:

Интенты и фильтры интентов
Узнайте, как использовать API Intent для активации компонентов приложения, таких как действия и сервисы, и как сделать компоненты приложения доступными для других приложений.
Общие сведения о действиях
Узнайте, как создать экземпляр класса Activity, который предоставляет отдельный экран в вашем приложении с пользовательским интерфейсом.
Обзор ресурсов приложения
Узнайте, как устроены приложения для Android, чтобы разделять ресурсы и код приложения, в том числе как предоставлять альтернативные ресурсы для определенных конфигураций устройств.

Также вас может заинтересовать:

Общие сведения о совместимости устройств
Узнайте, как Android работает на разных типах устройств, как оптимизировать приложение для каждого из них и как ограничить доступность приложения для определенных устройств.
Разрешения на устройствах Android
Узнайте, как Android ограничивает доступ приложений к определенным API с помощью системы разрешений, которая требует согласия пользователя на использование этих API.