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

В этом уроке показано, как добавлять и удалять геозоны, а затем отслеживать переходы между геозонами с помощью BroadcastReceiver .
Примечание: на устройствах Wear API геозонирования неэффективно используют энергию. Мы не рекомендуем использовать эти API на устройствах Wear. Для получения дополнительной информации ознакомьтесь с разделом «Экономия энергии и заряда батареи» .
Настройка мониторинга геозон
Первым шагом при запросе мониторинга геозон является получение необходимых разрешений. Для использования геозонирования ваше приложение должно запросить следующее:
-
ACCESS_FINE_LOCATION -
ACCESS_BACKGROUND_LOCATION, если ваше приложение ориентировано на Android 10 (уровень API 29) или выше.
Для получения более подробной информации ознакомьтесь с руководством по запросу разрешения на определение местоположения .
Если вы хотите использовать BroadcastReceiver для отслеживания переходов между геозонами, добавьте элемент, указывающий имя сервиса. Этот элемент должен быть дочерним элементом элемента <application> :
<application android:allowBackup=">true" < ... receiver android:name=".GeofenceBroa>d<castReceiver>"/ application/
Для доступа к API определения местоположения необходимо создать экземпляр клиента Geofencing. Инструкции по подключению клиента см. здесь:
Котлин
lateinit var geofencingClient: GeofencingClient override fun onCreate(savedInstanceState: Bundle?) { // ... geofencingClient = LocationServices.getGeofencingClient(this) }
Java
private GeofencingClient geofencingClient; @Override public void onCreate(Bundle savedInstanceState) { // ... geofencingClient = LocationServices.getGeofencingClient(this); }
Создавайте и добавляйте геозоны.
Вашему приложению необходимо создавать и добавлять геозоны, используя класс Builder API местоположения для создания объектов Geofence и вспомогательный класс для их добавления. Кроме того, для обработки интентов, отправляемых службами определения местоположения при переходе между геозонами, вы можете определить PendingIntent как показано в этом разделе.
Примечание: на устройствах для одного пользователя действует ограничение в 100 геозон на приложение. На устройствах для нескольких пользователей ограничение составляет 100 геозон на приложение на одного пользователя устройства.
Создание объектов геозоны
Сначала используйте Geofence.Builder для создания геозоны, задав желаемый радиус, длительность и типы переходов. Например, чтобы заполнить объект списка:
Котлин
geofenceList.add(Geofence.Builder() // Set the request ID of the geofence. This is a string to identify this // geofence. .setRequestId(entry.key) // Set the circular region of this geofence. .setCircularRegion( entry.value.latitude, entry.value.longitude, Constants.GEOFENCE_RADIUS_IN_METERS ) // Set the expiration duration of the geofence. This geofence gets automatically // removed after this period of time. .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS) // Set the transition types of interest. Alerts are only generated for these // transition. We track entry and exit transitions in this sample. .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT) // Create the geofence. .build())
Java
geofenceList.add(new Geofence.Builder() // Set the request ID of the geofence. This is a string to identify this // geofence. .setRequestId(entry.getKey()) .setCircularRegion( entry.getValue().latitude, entry.getValue().longitude, Constants.GEOFENCE_RADIUS_IN_METERS ) .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS) .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT) .build());
В этом примере данные извлекаются из файла констант. На практике приложения могут динамически создавать геозоны на основе местоположения пользователя.
Укажите геозоны и начальные триггеры.
В следующем фрагменте кода используется класс GeofencingRequest и вложенный к нему класс GeofencingRequestBuilder для указания геозон, которые необходимо отслеживать, и для настройки способа запуска связанных событий геозон:
Котлин
private fun getGeofencingRequest(): GeofencingRequest { return GeofencingRequest.Builder().apply { setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER) addGeofences(geofenceList) }.build() }
Java
private GeofencingRequest getGeofencingRequest() { GeofencingRequest.Builder builder = new GeofencingRequest.Builder(); builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER); builder.addGeofences(geofenceList); return builder.build(); }
В этом примере показано использование двух триггеров геозоны. Переход GEOFENCE_TRANSITION_ENTER срабатывает, когда устройство входит в геозону, а переход GEOFENCE_TRANSITION_EXIT — когда устройство выходит из геозоны. Указание INITIAL_TRIGGER_ENTER сообщает службам определения местоположения, что GEOFENCE_TRANSITION_ENTER должен срабатывать, если устройство уже находится внутри геозоны.
Во многих случаях предпочтительнее использовать параметр INITIAL_TRIGGER_DWELL , который запускает события только тогда, когда пользователь останавливается на заданное время внутри геозоны. Такой подход может помочь уменьшить количество «спам-уведомлений», возникающих из-за большого количества сообщений, когда устройство ненадолго входит в геозону и выходит из нее. Еще одна стратегия для достижения наилучших результатов от геозон — установить минимальный радиус в 100 метров. Это помогает учесть точность определения местоположения в типичных сетях Wi-Fi, а также снизить энергопотребление устройства.
Определите широковещательный приемник для переходов между геозонами.
Intent , отправленный службой определения местоположения, может запускать различные действия в вашем приложении, но он не должен запускать активность или фрагмент, поскольку компоненты должны становиться видимыми только в ответ на действия пользователя. Во многих случаях BroadcastReceiver — хороший способ обработки перехода между геозонами. BroadcastReceiver получает обновления при возникновении события, например, при переходе в геозону или из нее, и может запускать длительные фоновые процессы.
Следующий фрагмент кода показывает, как определить PendingIntent , который запускает BroadcastReceiver :
Котлин
class MainActivity : AppCompatActivity() { // ... private val geofencePendingIntent: PendingIntent by lazy { // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling // addGeofences() and removeGeofences(). val flags = PendingIntent.FLAG_UPDATE_CURRENT if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { // Starting on Android S+ the pending intent has to be mutable. flags or PendingIntent.FLAG_MUTABLE } val intent = Intent(this, GeofenceBroadcastReceiver::class.java) PendingIntent.getBroadcast(this, 0, intent, flags) } }
Java
public class MainActivity extends AppCompatActivity { // ... private PendingIntent getGeofencePendingIntent() { // Reuse the PendingIntent if we already have it. if (geofencePendingIntent != null) { return geofencePendingIntent; } // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling // addGeofences() and removeGeofences(). int flags = PendingIntent.FLAG_UPDATE_CURRENT; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { // Starting on Android S+ the pending intent has to be mutable. flags |= PendingIntent.FLAG_MUTABLE; } Intent intent = new Intent(this, GeofenceBroadcastReceiver.class); geofencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, flags); return geofencePendingIntent; } }
Добавить геозоны
Для добавления геозон используйте метод . Предоставьте объект GeofencingClient.addGeofences()GeofencingRequest и PendingIntent . Следующий фрагмент кода демонстрирует обработку результатов:
Котлин
geofencingClient?.addGeofences(getGeofencingRequest(), geofencePendingIntent)?.run { addOnSuccessListener { // Geofences added // ... } addOnFailureListener { // Failed to add geofences // ... } }
Java
geofencingClient.addGeofences(getGeofencingRequest(), getGeofencePendingIntent()) .addOnSuccessListener(this, new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { // Geofences added // ... } }) .addOnFailureListener(this, new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Failed to add geofences // ... } });
Обработка переходов между геозонами
Когда службы определения местоположения обнаруживают, что пользователь вошел в геозону или вышел из нее, они отправляют Intent содержащийся в PendingIntent , который вы указали в запросе на добавление геозон. Приемник широковещательной рассылки, такой как GeofenceBroadcastReceiver замечает, что Intent был вызван, и может затем получить событие геозонирования из Intent, определить тип перехода между геозонами и определить, какая из определенных геозон была активирована. Приемник широковещательной рассылки может дать указание приложению начать фоновую работу или, при желании, отправить уведомление в качестве выходного сигнала.
Примечание: На Android 8.0 (уровень API 26) и выше, если приложение работает в фоновом режиме, отслеживая геозону, устройство реагирует на события геозоны каждые несколько минут. Чтобы узнать, как адаптировать ваше приложение к этим ограничениям по времени отклика, см. раздел «Ограничения на определение местоположения в фоновом режиме» .
В следующем фрагменте кода показано, как определить BroadcastReceiver , который отправляет уведомление при переходе между геозонами. Когда пользователь нажимает на уведомление, появляется основная активность приложения:
Котлин
class GeofenceBroadcastReceiver : BroadcastReceiver() { // ... override fun onReceive(context: Context?, intent: Intent?) { val geofencingEvent = GeofencingEvent.fromIntent(intent) if (geofencingEvent.hasError()) { val errorMessage = GeofenceStatusCodes .getStatusCodeString(geofencingEvent.errorCode) Log.e(TAG, errorMessage) return } // Get the transition type. val geofenceTransition = geofencingEvent.geofenceTransition // Test that the reported transition was of interest. if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) { // Get the geofences that were triggered. A single event can trigger // multiple geofences. val triggeringGeofences = geofencingEvent.triggeringGeofences // Get the transition details as a String. val geofenceTransitionDetails = getGeofenceTransitionDetails( this, geofenceTransition, triggeringGeofences ) // Send notification and log the transition details. sendNotification(geofenceTransitionDetails) Log.i(TAG, geofenceTransitionDetails) } else { // Log the error. Log.e(TAG, getString(R.string.geofence_transition_invalid_type, geofenceTransition)) } } }
Java
public class GeofenceBroadcastReceiver extends BroadcastReceiver { // ... protected void onReceive(Context context, Intent intent) { GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent); if (geofencingEvent.hasError()) { String errorMessage = GeofenceStatusCodes .getStatusCodeString(geofencingEvent.getErrorCode()); Log.e(TAG, errorMessage); return; } // Get the transition type. int geofenceTransition = geofencingEvent.getGeofenceTransition(); // Test that the reported transition was of interest. if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) { // Get the geofences that were triggered. A single event can trigger // multiple geofences. List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences(); // Get the transition details as a String. String geofenceTransitionDetails = getGeofenceTransitionDetails( this, geofenceTransition, triggeringGeofences ); // Send notification and log the transition details. sendNotification(geofenceTransitionDetails); Log.i(TAG, geofenceTransitionDetails); } else { // Log the error. Log.e(TAG, getString(R.string.geofence_transition_invalid_type, geofenceTransition)); } } }
После обнаружения события перехода через PendingIntent , BroadcastReceiver получает тип перехода геозоны и проверяет, является ли это одним из событий, используемых приложением для запуска уведомлений — в данном случае это GEOFENCE_TRANSITION_ENTER или GEOFENCE_TRANSITION_EXIT . Затем сервис отправляет уведомление и регистрирует подробности перехода.
Остановить мониторинг геозон
Отключение мониторинга геозон, когда он больше не нужен или нежелателен, может помочь сэкономить заряд батареи и ресурсы процессора устройства. Вы можете остановить мониторинг геозон в основной активности, используемой для добавления и удаления геозон; удаление геозоны останавливает его немедленно. API предоставляет методы для удаления геозон либо по идентификаторам запросов, либо путем удаления геозон, связанных с заданным PendingIntent .
Следующий фрагмент кода удаляет геозоны по значению PendingIntent , прекращая отправку всех дальнейших уведомлений при входе или выходе устройства из ранее добавленных геозон:
Котлин
geofencingClient?.removeGeofences(geofencePendingIntent)?.run { addOnSuccessListener { // Geofences removed // ... } addOnFailureListener { // Failed to remove geofences // ... } }
Java
geofencingClient.removeGeofences(getGeofencePendingIntent()) .addOnSuccessListener(this, new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { // Geofences removed // ... } }) .addOnFailureListener(this, new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Failed to remove geofences // ... } });
Вы можете комбинировать геозонирование с другими функциями, учитывающими местоположение, такими как периодическое обновление данных о местоположении. Для получения дополнительной информации см. другие уроки в этом курсе.
Используйте лучшие практики для геозонирования.
В этом разделе изложены рекомендации по использованию геозонирования с API определения местоположения для Android.
Снизьте энергопотребление
Для оптимизации энергопотребления в приложениях, использующих геозонирование, можно применять следующие методы:
Установите более высокое значение параметра « Время отклика уведомлений» . Это позволит снизить энергопотребление за счет увеличения задержки оповещений о геозонах. Например, если вы установите значение времени отклика в пять минут, ваше приложение будет проверять наличие уведомлений о входе или выходе только раз в пять минут. Установка более низких значений не обязательно означает, что пользователи будут уведомлены в течение этого периода времени (например, если вы установите значение в 5 секунд, получение уведомления может занять немного больше времени).
Используйте больший радиус геозоны для мест, где пользователь проводит значительное количество времени, например, дома или на работе. Хотя больший радиус напрямую не снижает энергопотребление, он уменьшает частоту проверки приложением входа или выхода, что эффективно снижает общее энергопотребление.
Выберите оптимальный радиус для вашей геозоны.
Для достижения наилучших результатов минимальный радиус геозоны следует установить в пределах 100–150 метров. При наличии Wi-Fi точность определения местоположения обычно составляет от 20 до 50 метров. При наличии возможности определения местоположения внутри помещений диапазон точности может составлять всего 5 метров. Если вы не уверены в наличии возможности определения местоположения внутри геозоны, предполагайте, что точность определения местоположения по Wi-Fi составляет около 50 метров.
Когда определение местоположения по Wi-Fi недоступно (например, при движении по сельской местности), точность определения местоположения снижается. Диапазон точности может составлять от нескольких сотен метров до нескольких километров. В таких случаях следует создавать геозоны с большим радиусом.
Объясните пользователям, почему ваше приложение использует геозонирование.
Поскольку ваше приложение получает доступ к данным о местоположении в фоновом режиме при использовании геозонирования, подумайте, какие преимущества оно предоставляет пользователям. Четко объясните им, почему вашему приложению необходим этот доступ, чтобы повысить понимание и прозрачность для пользователей.
Для получения дополнительной информации о передовых методах обеспечения доступа к данным о местоположении, включая геозонирование, см. страницу с передовыми методами обеспечения конфиденциальности .
Используйте тип перехода "задержка", чтобы уменьшить количество спама в уведомлениях.
Если вы получаете большое количество оповещений при кратковременном проезде мимо геозоны, лучший способ уменьшить их количество — использовать тип перехода GEOFENCE_TRANSITION_DWELL вместо GEOFENCE_TRANSITION_ENTER . Таким образом, оповещение о пребывании в геозоне будет отправлено только тогда, когда пользователь остановится внутри геозоны на заданный период времени. Вы можете выбрать продолжительность, установив задержку праздношатания .
Повторную регистрацию геозон следует проводить только при необходимости.
Зарегистрированные геозоны хранятся в процессе com.google.process.location принадлежащем пакету com.google.android.gms . Приложению не нужно ничего делать для обработки следующих событий, поскольку система восстанавливает геозоны после этих событий:
- Сервисы Google Play обновлены.
- Службы Google Play завершаются и перезапускаются системой из-за нехватки ресурсов.
- Процесс определения местоположения завершается с ошибкой.
Если после следующих событий геозоны по-прежнему необходимы, приложению потребуется их повторная регистрация, поскольку в следующих случаях система не сможет восстановить геозоны:
- Устройство перезагружается. Приложение должно отслеживать завершение загрузки устройства, а затем повторно зарегистрировать необходимые геозоны.
- Приложение удалено и установлено заново.
- Данные приложения удалены.
- Данные сервисов Google Play удалены.
- Приложение получило предупреждение
GEOFENCE_NOT_AVAILABLE. Обычно это происходит после отключения NLP (поставщика данных о местоположении в сети Android).
Устранение неполадок, связанных с событием входа в геозону.
Если геозоны не срабатывают при входе устройства в геозону (предупреждение GEOFENCE_TRANSITION_ENTER не появляется), сначала убедитесь, что ваши геозоны зарегистрированы правильно, как описано в этом руководстве.
Вот несколько возможных причин, по которым оповещения могут работать некорректно:
- Точное определение местоположения внутри вашей геозоны недоступно, или ваша геозона слишком мала. На большинстве устройств служба геозон использует только сетевое местоположение для активации геозоны. Сервис использует такой подход, потому что сетевое местоположение потребляет гораздо меньше энергии, получение точных данных занимает меньше времени, и, что наиболее важно, оно доступно внутри помещений.
На устройстве отключен Wi-Fi. Включение Wi-Fi может значительно повысить точность определения местоположения, поэтому, если Wi-Fi отключен, ваше приложение может никогда не получать оповещения о геозоне в зависимости от нескольких настроек, включая радиус геозоны, модель устройства или версию Android. Начиная с Android 4.3 (уровень API 18), мы добавили возможность «Режим только сканирования Wi-Fi», который позволяет пользователям отключать Wi-Fi, но при этом получать точные данные о местоположении в сети. Рекомендуется предлагать пользователю включить Wi-Fi или режим только сканирования Wi-Fi и предоставить ярлык для включения Wi-Fi или включения режима только сканирования Wi-Fi, если оба режима отключены. Используйте SettingsClient , чтобы убедиться, что системные настройки устройства правильно настроены для оптимального определения местоположения.
Примечание: Если ваше приложение ориентировано на Android 10 (уровень API 29) или выше, вы не можете напрямую вызывать
WifiManager.setEnabled()если только ваше приложение не является системным приложением или контроллером политик устройства (DPC) . Вместо этого используйте панель настроек .- Внутри вашей геозоны отсутствует надежное сетевое соединение. Если нет надежного подключения к сети передачи данных, оповещения могут не генерироваться. Это связано с тем, что служба геозоны зависит от поставщика данных о местоположении, которому, в свою очередь, требуется подключение к сети передачи данных.
- Уведомления могут приходить с задержкой. Сервис геозонирования не запрашивает местоположение постоянно, поэтому ожидайте некоторой задержки при получении уведомлений. Обычно задержка составляет менее 2 минут, а если устройство находилось в движении, то еще меньше. Если действуют ограничения на фоновое определение местоположения , задержка составляет в среднем около 2-3 минут. Если устройство находилось в неподвижном состоянии в течение значительного периода времени, задержка может увеличиться (до 6 минут).
Дополнительные ресурсы
Чтобы узнать больше о геозонировании, ознакомьтесь со следующими материалами:
Образцы
Пример приложения для создания и мониторинга геозон.