Урок 12: Компоненты Android: Activity, Service, Content Provider, Broadcast Receiver 

12 1 scaled - Android [Kotlin] для начинающих

Смотрите видеоурок бесплатно на удобной для вас платформе:

Понятие — основные компоненты

Здравствуйте. Поговорим про основные компоненты андройд. У начинающих разработчиков возникнет резонный вопрос: “А зачем мне эта информация и что это вообще за компоненты”.

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

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

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

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

Окей, и еще один организационный момент. Так как проект создавался в старой версии Android Studio, некоторые файлы устарели. Я провел небольшой рефакторинг и пофиксил конфигурационные файлы gradle. Полный актуальный код забирайте в моем телеграм канале.

Теперь к компонентам! Надеюсь с вопросом “зачем” примерно разобрались, разберемся что это такое.

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

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

Существует 4 базовых компонента: Activity, Service, Content Provider, Broadcast Receiver.

Activity

Activity фактически представляет собой экран (или UI – User Interface) – то, что пользователь видит и с чем может взаимодействовать.

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

Даже если у нас будет несколько Activity, система запустит именно прописанный здесь MainActivity. Это произойдет благодаря специальному тэгу <intent-filter>. В нем прописаны action MAIN и category LAUNCHER. Таким образом мы явно можем указывать с какого экрана должно стартовать приложение. Про интенты и интент-фильтры мы поговорим в уроке, который будет полностью посвящен компоненту Activity.

Service

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

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

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

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

onBind() – метод класса Service

Метод служит для создания связи с другими компонентами, например, с Activity. Это позволяет напрямую из Активити получать данные или управлять методами сервисами условно в режиме реального времени. Но, если привязка не требуется – то есть сервис не предоставляет интерфейс для других компонентов – метод должен возвращать null.

class MusicService : Service() {
    
    override fun onBind(p0: Intent?): IBinder? {
        return null
    }
    
}

У Service есть еще ряд необязательных методов для определения, которые используются в зависимости от ситуации. Их можно посмотреть вызвав меню Generate с помощью комбинации клавиш cmd/ctrl + N → Override methods. В этом обзорном уроке мы не рассматриваем частные случаи, но для примера здесь можем переопределить еще вот такие три метода: onStart(), onStartCommand() и onDestroy().

onStart() – метод класса Service

Метод onStart() является основной точкой инициализации сервиса. Он вызывается системой при создании сервиса, до того как сервис начнёт выполнение любых команд, переданных в onStartCommand() или перед тем, как сервис будет связан с компонентом через onBind().

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

onStartCommand() – метод класса Service

onStartCommand() вызывается системой каждый раз, когда какой-нибудь компонент (тот же, Activity) пытается запустить сервис, используя startService(). Здесь выполняется основная логика работы сервиса. Переопределение этого метода позволяет управлять тем, как сервис будет реагировать на каждый вызов startService. То есть фактически мы прописываем здесь нашу основную кастомную логику его работы.

onDestroy() – метод класса Service

Метод onDestroy() вызывается при уничтожении сервиса, это идеальное место для очистки ресурсов, таких как закрытие потоков данных или остановка музыки. Это последний метод, который вызывает система перед тем, как сервис будет уничтожен.

class MusicService : Service() {

    override fun onBind(p0: Intent?): IBinder? {
        return null
    }

    override fun onStart(intent: Intent?, startId: Int) {
        super.onStart(intent, startId)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
    }
}

После создания сервиса не забудьте зарегистрировать его в манифесте.

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

Существуют всего 3 вида сервисов. Первые 2 из них не зависят от вышеупомянутой привязки, поэтому их называют Unbound сервисы. Вот они:

  • Foreground Service – работает видимо для пользователя, показывая уведомление в статус-баре, когда активен. Используется для выполнения важных для пользователя операций, которые не должны быть остановлены системой. Например, для воспроизведения музыки или для выполнения активного GPS-трекинга.
  • Background Service – такие сервисы не видны пользователю и об их существовании он может даже не догадываться. Например, какое-либо приложение может использовать такой сервис, чтобы обращаться с бекэнду, или же работать с файлами данного приложения. Стоит добавить, что с ограничениями, введенными в Android 8.0 (Oreo), система не может выполнять задачи в фоне, когда приложение не на переднем плане. Если только сервис не переведен в категорию Foreground Service. Это связано с повышением уровня безопасности и с экономией ресурсов батареи, так как фоновые процессы изрядно стали ее поджирать при бездействии устройства.
  • Bound Service – третий тип сервиса, который можно перевести примерно как “связанная служба”. Он предоставляет компоненту (например, Activity) возможность «привязаться» к сервису и взаимодействовать с ним через IPC (Inter-Process Communication) или возвращаемый IBinder объект. Именно этот тип вы видите в сигнатуре переопределенного метода здесь и при переходе в исходный Java класс. В джаве возвращаемый тип указывается перед названием функции. Привязанный сервис нужен, когда нужно больше контроля и большее взаимодействие с сервисом. Можно напрямую управлять сервисом, отправляя соответствующие методы из привязанных компонентов (условно, Активити). Сервис существует только пока существует привязка. Когда все клиенты отвязаны, сервис уничтожается.

Хорошо. Для знакомства с сервисами этого пока будет достаточно.

Content Provider

Далее. Content Provider можно перевести, как “поставщик контента”, но это только для дополнительного понимания. Называть компоненты и технологии лучше оригинальными названиями, как это принято у разработчиков.

Это компонент, который управляет доступом к данным приложения и предоставляет их другим приложениям. Его можно сравнить с почтовым ящиком, куда одни приложения кладут данные (например, контакты или фотографии), а другие могут их оттуда взять, если знают адрес ящика.

Область применения довольно широкая. Например, для совместного использования данных. Если одно приложение хочет предоставить свои данные другим приложениям (например, системное приложение “Контакты” предоставляет доступ к списку контактов), оно использует Content Provider как безопасный способ поделиться ими.

Кроме того прелесть провайдера заключается в стандартизации доступа к данным. Content Provider предоставляет данные через стандартизированные запросы. Например, чтобы получить данные, не нужно знать, как они хранятся — в базе данных, файле или ещё как-то. Интерфейс Content Provider абстрагирует эти детали через CRUD-операции (Это аббревиатура от Create, Read, Update, Delete), реализуемые через переопределённые методы. Сейчас вы их увидите.

Предположим, мы собираемся создать свое приложение с контактами и предоставлять доступ для других приложений системы. Для этого понадобится создать класс провайдера, например, ContactsProvider, отнаследованный от ContentProvider(). Далее нам предлагается реализовать целых 6 обязательных методов.

class ContactsProvider : ContentProvider() {
    override fun onCreate(): Boolean {
        TODO("Not yet implemented")
    }

    override fun query(
        p0: Uri,
        p1: Array<out String>?,
        p2: String?,
        p3: Array<out String>?,
        p4: String?
    ): Cursor? {
        TODO("Not yet implemented")
    }

    override fun getType(p0: Uri): String? {
        TODO("Not yet implemented")
    }

    override fun insert(p0: Uri, p1: ContentValues?): Uri? {
        TODO("Not yet implemented")
    }

    override fun delete(p0: Uri, p1: String?, p2: Array<out String>?): Int {
        TODO("Not yet implemented")
    }

    override fun update(p0: Uri, p1: ContentValues?, p2: String?, p3: Array<out String>?): Int {
        TODO("Not yet implemented")
    }
}

Кратко пройдемся по их назначению.

  1. onCreate(): Инициализирует провайдер. Здесь обычно выполняется настройка базы данных или других ресурсов. Возвращает true, если провайдер был успешно создан, и false, если нет.
  2. query(Uri, Array?, String?, Array?, String?): Используется для выполнения запросов к данным. В зависимости от URI, может извлекать данные из различных таблиц БД или других источников. URI, или Uniform Resource Identifier, — называют просто способ идентификации ресурса или объекта в сети или на устройстве. Можно сравнить его с адресом, который указывает, где что-то находится или как получить доступ к этому «чему-то”.
  3. getType(Uri): Метод представляет собой способ указать тип данных, которые возвращает URI. Тип данных нужен для обработки запросов от других приложений, чтобы они могли определить тип контента и понять как его обрабатывать.
  4. insert(Uri, ContentValues?): Вставляет новые данные в провайдер. ContentValues содержит значения, которые должны быть вставлены. Возвращает URI новой записи.
  5. delete(Uri, String?, Array?): Удаляет данные из провайдера по переданному URI. Может удалять одну или несколько записей и возвращает количество удалённых записей.
  6. update(Uri, ContentValues?, String?, Array?): Обновляет существующие данные. Как и delete, работает на основе URI. Возвращает количество обновлённых записей.

Broadcast Receiver

Окей. Broadcast Receiver – четвертый основной компонент OS Android. Его можно воспринимать как приемник событий системы или других приложений. Его предназначение ожидать сигнал от системы или другого приложения, чтобы его поймать, обработать и запустить какое-нибудь действие в вашем приложении.

Пример – вы разговариваете по Телеграм с кем-то по аудио/видео связи и видите сообщение “У такого-то пользователя низкий заряд батареи”. Как это может происходить. На клиенте вашего собеседника компонент приложения Broadcast Receiver перехватывает сообщение системы о том, что заряд батареи низкий. Далее он его обрабатывает и выполняет соответствующую задачу – условно передать эту информацию на клиентское приложение собеседника.

Вот как он выглядит в коде. Создадим класс бродкаст ресивера BatteryLevelReceiver. При наследовании от класса BroadcastReceiver нас попросят имплементировать обязательный метод onReceive. Именно этот метод вызывается, когда Broadcast Receiver получает сообщение, которое он ожидал.

class BatteryLevelReceiver : BroadcastReceiver() {
    
    override fun onReceive(p0: Context?, p1: Intent?) {
        TODO("Not yet implemented")
    }
    
}

Но как нам сообщить системе, что мы ожидаем для перехвата какое-то конкретное событие? Для начала компонент необходимо зарегистрировать в OS. И есть два способа регистрации: статический и динамический.

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

Это делается с помощью интент-фильтров. Внутри в тега action указывается название системного действия, которое нужно отследить. В данном случае для примера пропишем экшн BATTERY_LOW.

<receiver
    android:name=".BatteryLevelReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BATTERY_LOW" />
    </intent-filter>
</receiver>

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

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

Заключение

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

Также обращаю внимание, что эти компоненты мы не создаем самостоятельно. То есть мы не пишем что-то вроде new “название_компонента” и круглые скобки primary конструктора – нет. Мы только можем их объявить в манифесте и попросить систему создать требуемые компоненты для нас. А для управления ими и добавления специфичной логики нам с вами нужно реализовывать собственные классы и переопределять методы, наследуемые от выбранных компонентов.

Именно поэтому они и называются “основные компоненты операционной системы Android”. То есть как бы принадлежащие системе.

Для тех, кто собрался стать Android-разработчиком

Пошаговаясхема
Пошаговая
схема

Описание процесса обучения от основ Kotlin до Android-разработчика

Бесплатныеуроки
Бесплатные
уроки

Авторский бесплатный курс по основам языка программирования Kotlin

Обучающийбот
Обучающий
бот

Тренажер и самоучитель по Котлин – бесплатные тесты и практика

Поделиться уроком

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *