Добавлен: 29.10.2018
Просмотров: 48047
Скачиваний: 190
906
Глава 10. Изучение конкретных примеров: Unix, Linux и Android
Чтобы увидеть, как можно воспользоваться службой в качестве точки связи для
взаимодействия с другими приложениями, давайте предположим, что нам нужно
расширить уже существующую SyncService, заполучив API-функцию, позволяющую
другим приложениям управлять ее интервалом синхронизации. Нам нужно будет
определить для этой API-функции AIDL-интерфейс, подобный тому, что показан
в листинге 10.6.
Листинг 10.6. Интерфейс для управления интервалом синхронизации службы sync
package com.example.email
interface ISyncControl {
int getSyncInterval();
void setSyncInterval(int seconds);
}
Чтобы воспользоваться этим, другой процесс может привязаться к нашей прикладной
службе, получив доступ к ее интерфейсу. Тем самым будет создана связь между двумя
приложениями (рис. 10.38). Этот процесс проходит следующие этапы:
1. Клиентское приложение сообщает диспетчеру активностей, что оно намеревается
привязаться к службе.
2. Если служба еще не создана, диспетчер активностей создает ее в процессе при-
ложения службы.
3. Служба возвращает экземпляр IBinder для своего интерфейса диспетчеру актив-
ностей, который теперь удерживает этот IBinder в своей записи ServiceRecord.
4. После того как диспетчер активностей получил IBinder службы, этот экземпляр
может быть отправлен в адрес исходного клиентского приложения.
5. Теперь, когда у клиентского приложения имеется IBinder службы, оно может про-
должить выполнение своего намерения, сделав на свое усмотрение любые непо-
средственные вызовы к интерфейсу службы.
2. Создает
3. Возвра-
щает
4. Отправ-
ляет
1. Привя-
зывает
Процесс клиентского приложения
5. Вызывает службу
SyncService
IBinder
IBinder
IBinder
IBinder
IBinder
ServiceRecord
(SyncService)
Процесс почтового приложения
Диспетчер активностей
в процессе system_server
ОСТ
АНОВ-
ЛЕННАЯ
Рис. 10.38. Привязка к прикладной службе
10.8. Android
907
Получатели
Получатель
(receiver) принимает случающиеся события (как правило, внешние) обыч-
но в фоновом режиме и вне обычного взаимодействия с пользователем. Концептуально
получатели — это то же самое, что и приложение, явным образом зарегистрированное
для обратного вызова при наступлении какого-нибудь интересного события (выдан
сигнал тревоги, произошло изменение в передаче данных и т. д.), от которого не требу-
ется, чтобы оно обязательно оставалось в работающем состоянии для приема события.
Пример манифеста почтового приложения, показанный в листинге 10.5, содержит
получатель для приложения, чтобы выяснить, когда устройство начинает испытывать
дефицит памяти, и остановить синхронизацию электронной почты (которая может
потреблять больше памяти). Когда устройство начинает испытывать дефицит памяти,
система разошлет сигнал с кодом низкого уровня свободной памяти, чтобы доставить
его всем получателям, заинтересованным в этом событии.
На рис. 10.39 показано, как такая рассылка обрабатывается диспетчером активностей
для доставки заинтересованным получателям. Сначала диспетчеру пакетов отправляет-
ся требование о представлении списка всех заинтересованных в событии получателей,
затем этот список помещается в запись BroadcastRecord, представляющую рассылку. За-
тем диспетчер активностей приступит к обходу всех записей в списке, создавая каждый
связанный с ним процесс приложения и выполняя соответствующий класс получателя.
Диспетчер активностей
в процессе system_server
Процесс приложения Календарь
Процесс почтового приложения
Процесс приложения браузера
SyncControlReceiver
SyncControlReceiver
CleanupReceiver
BroadcastRecord
DEVICE_STORAGE_LOW
SyncControlReceiver
(Calendar app)
SyncControlReceiver
(Email app)
CleanupReceiver
(Browser app)
Рис. 10.39. Отправка рассылки получателям приложений
Получатели работают только как разовые операции. Когда происходит событие, систе-
ма находит всех заинтересованных в нем получателей и доставляет им событие, а они
после потребления события завершают свою работу. Здесь нет записи ReceiverRecord,
подобной той, что мы видели для других компонентов приложения, поскольку отдель-
но взятый получатель является всего лишь переходным объектом на время действия
отдельной рассылки. При каждой отправке получателю новой рассылки создается
новый экземпляр класса этого получателя.
908
Глава 10. Изучение конкретных примеров: Unix, Linux и Android
Поставщики контента
Последним нашим компонентом приложения будет поставщик контента (content
provider), который является основным механизмом, используемым приложением для
обмена данными с другими приложениями. Все взаимодействия с поставщиком контен-
та осуществляются через унифицированные индикаторы ресурса (URI), с использова-
нием формата content: схема. Полномочия URI используются для поиска правильной
реализации поставщика контента, с которым требуется наладить взаимодействие.
Например, в нашем почтовом приложении из листинга 10.5 поставщик контекста
указывает, что его полномочие — это com.example.email.provider.email. Стало быть, URI-
индикаторы, работающие на этот поставщик контента, должны начинаться с
content://com.example.email.provider.email/
Суффикс для этого URI интерпретируется самим поставщиком для определения того,
какие данные внутри него будут доступны. В этом примере обычным соглашением
будет то, что URI
content://com.example.email.provider.email/messages
означает список всех почтовых сообщений, а
content://com.example.email.provider.email/messages/1
предоставляет доступ к одному сообщению с номером ключа 1.
Для взаимодействия с поставщиком контента приложения всегда проходят через ис-
пользование системного API-интерфейса по имени ContentResolver, где у большинства
методов имеется начальный URI-аргумент, показывающий данные, с которыми нужно
работать. Одним из наиболее востребованных методов ContentResolver является query,
осуществляющий запрос к базе данных по заданному URI и возвращающий Cursor для
извлечения структурированных результатов. Например, извлечение сводки обо всех
доступных почтовых сообщениях будет иметь примерно следующий вид:
query("content://com.example.email.provider.email/messages")
Хотя это не похоже на приложения, но то, что на самом деле происходит, когда они
используют поставщиков контента, имеет много общего с привязками к службам. Поря-
док обработки системой нашего примера запроса показан на рис. 10.40:
1. Приложение вызывает ContentResolver.query для начала операции.
2. URI-полномочия вручаются диспетчеру активностей, чтобы он нашел (через дис-
петчер пакетов) соответствующего поставщика контента.
3. Поставщик контента еще не работает, поэтому он создается.
4. После создания поставщик контента возвращает диспетчеру активностей свой
IBinder, реализуя тем самым системный интерфейс IContentProvider.
5. IBinder поставщика контента возвращается в распознаватель контента —Content-
Resolver.
6. Теперь распознаватель контента может завершить исходную операцию query,
вызвав соответствующий метод в отношении AIDL-интерфейса, возвращающий
результат Cursor.
Поставщики контента являются одним из основных механизмов для осуществления
взаимодействия между приложениями. Например, если вернуться к ранее описанной
10.8. Android
909
Диспетчер активностей
в процессе system_server
Процесс почтового приложения
ProviderRecord
(EmailProvider)
EmailProvider
ContentResolver
1. query()
Процесс клиентского приложения
3. Создает
IBinder
IContentProvider.Stub
IContentProvider.Proxy
4. Возвращает
2. Ищет
полномочия
IBinder
IBinder
6. query()
5. Возвращает
Рис. 10.40. Взаимодействие с поставщиком контента
системе использования общего контента, показанной на рис. 10.35, то поставщики кон-
тента являются способом реального переноса данных. Полностью ход этой операции
можно описать следующим образом:
1. Создается и отправляется системе запрос на общий контент, включающий URI
данных, подлежащих совместному использованию.
2. Система требует от ContentResolver MIME-тип данных, указанных в этом URI. Это
действие во многом похоже на только что рассмотренный метод query, но требует
от поставщика контента возвратить строку MIME-типа для URI.
3. Система ищет все активности, которые могут получить данные этого идентифи-
цированного MIME-типа.
4. Пользователю показывается пользовательский интерфейс для выбора одного из
возможных получателей.
5. Когда одна из активностей выбрана, система ее запускает.
6. Активность, обрабатывающая общий контент, получает URI совместно исполь-
зуемых данных, извлекает его данные с помощью ContentResolver и выполняет со-
ответствующую операцию: создает адрес электронной почты, сохраняет его и т. д.
10.8.9. Намерения
Деталь, которую мы еще не рассматривали в манифесте приложения, показанном
в листинге 10.5, относится к тегам <intent-filter>, включенным вместе с объявлением
активностей и получателя. Это часть существующего в Android свойства «намерение»
(intent), являющегося краеугольным камнем способа, который позволяет разным при-
ложениям идентифицировать друг друга, обеспечивая возможности взаимодействия
и совместной работы.
Намерение является механизмом, который используется системой Android для ис-
следования и идентификации активностей, получателей и служб. Он чем-то похож на
путь поиска оболочки Linux, который оболочка использует для сквозного просмотра
910
Глава 10. Изучение конкретных примеров: Unix, Linux и Android
нескольких возможных каталогов, чтобы найти исполняемый файл, соответствующий
указанному имени команды.
Есть два основных типа намерений: явные и неявные. Явное намерение (explicit intent)
напрямую идентифицирует один конкретный компонент приложения, в понятиях
оболочки Linux он является эквивалентом предоставления команде абсолютного пути.
Наиболее важной частью такого намерения является пара строк, называющая компо-
нент: имя пакета целевого приложения и имя класса компонента внутри этого прило-
жения. Теперь снова сошлемся на активность, показанную на рис. 10.32 в приложении,
чей манифест показан в листинге 10.5. Явным намерением этого компонента будет один
из пакетов по имени com.example.email и имя класса com.example.email.MailMainActivity.
Пакет и имя класса явного намерения являются вполне достаточной информацией для
уникальной идентификации целевого компонента, например основной почтовой актив-
ности, показанной на рис. 10.32. Из имени пакета диспетчер пакетов может вернуть все
необходимые сведения о приложении, например то, где найти его код. Из имени класса
мы узнаем, какую часть этого кода следует выполнить.
Неявное намерение
(implicit intent) описывает характеристики желаемых компо-
нентов, но не сами компоненты, в понятиях оболочки Linux оно эквивалентно предо-
ставлению оболочке одного только имени команды, которое она использует со своим
путем поиска для нахождения конкретной запускаемой команды. Этот процесс поиска
компонента, совпадающего с явным намерением, называется разрешением намерения
(intent resolution).
Основная возможность Android по совместному использованию объектов, ранее пока-
занная на рис. 10.35, иллюстрирующем совместное использования снимка, сделанного
пользователем при помощи камеры, в почтовом приложении, является хорошим при-
мером неявного намерения. В нем приложение камеры создает намерение, описываю-
щее то действие, которое должно быть выполнено, а система находит все активности,
которые потенциально могут выполнить это действие. Совместное использование
запрашивается через действие намерения android.intent.action.SEND, и в листинге 10.5
мы можем увидеть, что имеющаяся в почтовом приложении активность compose объ-
являет, что она может выполнить это действие.
У разрешения намерения может быть один из трех итогов:
соответствие не найдено;
найдено одно уникальное совпадение;
существует несколько активностей, способных справиться с намерением.
Пустое совпадение приведет либо к пустому результату, либо к исключению в зависимо-
сти от ожиданий вызвавшего это разрешение в данный момент. Если совпадение будет
иметь уникальный характер, система сможет тут же инициировать запуск нового явного
намерения. Если совпадение не будет уникальным, нам понадобится каким-то образом
разрешить намерение другим способом, приведя его к единственному результату.
Если намерение разрешается в несколько возможных активностей, мы не можем просто
запустить все эти активности — для запуска нужно выбрать только одну из них. Это
достигается с помощью особого приема в диспетчере пакетов. Если к нему поступила
просьба о разрешении намерения путем сведения его к единственному варианту, но он
обнаружил сразу несколько совпадений, он вместо этого передает разрешение намере-
ния специальной встроенной в систему активности, которая называется ResolverActivity.