Геозоны позволяют определить текущее местоположение пользователя и его близость к местам, которые могут его заинтересовать. Чтобы отметить интересующее местоположение, укажите его широту и долготу. Чтобы настроить близость к местоположению, добавьте радиус. Широта, долгота и радиус определяют геозону, создавая круговую область или забор вокруг интересующего местоположения.
У вас может быть несколько активных геозон, но не более 100 на приложение для каждого пользователя устройства. Для каждой геозоны можно настроить отправку событий входа и выхода или указать время, которое пользователь должен провести в геозоне, чтобы сработало событие. Вы можете ограничить время действия любой геозоны, указав срок действия в миллисекундах. По истечении срока действия геозоны Геолокация автоматически удаляет ее.
В этом уроке показано, как добавлять и удалять геозоны, а затем прослушивать переходы между ними с помощью BroadcastReceiver.
Примечание. На устройствах Wear OS Geofencing API неэффективно расходует заряд батареи. Мы не рекомендуем использовать эти API в Wear OS. Подробнее о том, как экономить заряд батареи…
Как настроить отслеживание геозоны
Первый шаг в запросе мониторинга геозоны – запросить необходимые разрешения. Чтобы использовать геозоны, приложение должно запрашивать следующие разрешения:
-
ACCESS_FINE_LOCATION -
ACCESS_BACKGROUND_LOCATIONесли приложение предназначено для Android 10 (уровень API 29) или более поздней версии.
Подробнее о том, как запрашивать доступ к данным о местоположении…
Если вы хотите использовать BroadcastReceiver для отслеживания переходов между геозонами, добавьте элемент, указывающий название сервиса. Этот элемент должен быть дочерним по отношению к элементу
<application>:
<application android:allowBackup="true"> ... <receiver android:name=".GeofenceBroadcastReceiver"/> <application/>
Чтобы получить доступ к Location API, необходимо создать экземпляр клиента Geofencing. Чтобы узнать, как подключить клиент:
Kotlin
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 для создания объектов Geofence и класс convenience для их добавления. Чтобы обрабатывать намерения, отправленные сервисами геолокации при пересечении геозоны, можно определить PendingIntent, как показано в этом разделе.
Примечание. На устройствах с одним пользователем ограничение составляет 100 геозон на приложение. На устройствах с добавлением пользователей ограничение составляет 100 геозон на приложение на пользователя.
Как создать объекты геозоны
Сначала создайте геозону с помощью
Geofence.Builder, задав нужный радиус, продолжительность и типы переходов. Например, чтобы заполнить объект списка, используйте следующий код:
Kotlin
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, чтобы указать геозоны для отслеживания и задать способ активации связанных событий геозоны:
Kotlin
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:
Kotlin
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.
В следующем фрагменте кода показано, как обрабатывать результаты:
Kotlin
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, и может получить событие геозоны из намерения, определить тип переходов геозоны и узнать, какая из определенных геозон была активирована. Широковещательный приемник может запустить фоновую задачу или отправить уведомление.
Примечание. На устройствах с Android 8.0 (уровень API 26) и более поздних версий, если приложение работает в фоновом режиме и отслеживает геозону, устройство реагирует на события геозоны каждые несколько минут. Подробнее о том, как адаптировать приложение к этим ограничениям…
В следующем фрагменте кода показано, как определить класс BroadcastReceiver, который отправляет уведомление при пересечении геозоны. Когда пользователь нажимает на уведомление, открывается основное действие приложения:
Kotlin
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, останавливая все дальнейшие уведомления, когда устройство входит в ранее добавленные геозоны или выходит из них:
Kotlin
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 // ... } });
Вы можете комбинировать геозоны с другими функциями, использующими данные о местоположении, например с периодическим обновлением местоположения. Подробную информацию вы найдете в других уроках этого курса.
Рекомендации по использованию геозон
В этом разделе приведены рекомендации по использованию геозоны с Location API для Android.
Снизить энергопотребление
Чтобы оптимизировать энергопотребление в приложениях, использующих геозоны, вы можете использовать следующие методы:
Увеличьте значение параметра Чувствительность уведомлений. Это позволяет снизить энергопотребление за счет увеличения задержки оповещений о геозонах. Например, если вы установите значение пять минут, приложение будет проверять наличие оповещений о входе или выходе только раз в пять минут. Если вы зададите меньшее значение, это не значит, что пользователи будут получать уведомления в течение этого периода времени (например, если вы зададите значение 5 секунд, получение оповещения может занять немного больше времени).
Используйте больший радиус геозоны для мест, где пользователь проводит значительное количество времени, например дома или на работе. Хотя больший радиус не снижает энергопотребление напрямую, он уменьшает частоту, с которой приложение проверяет вход или выход, эффективно снижая общее энергопотребление.
Выберите оптимальный радиус геозоны
Для достижения наилучших результатов минимальный радиус геозоны должен составлять от 100 до 150 метров. При наличии Wi-Fi точность геолокации обычно составляет от 20 до 50 метров. Если доступна геолокация в помещении, точность может составлять до 5 метров. Если вы не уверены, что в пределах геозоны доступно определение местоположения в помещении, считайте, что точность геолокации по Wi-Fi составляет около 50 метров.
Если определение местоположения по Wi-Fi недоступно (например, когда вы едете по сельской местности), точность геолокации снижается. Точность может варьироваться от нескольких сотен метров до нескольких километров. В таких случаях следует создавать геозоны с большим радиусом.
Объясните пользователям, зачем в приложении используется геозонирование.
Поскольку при использовании геозоны приложение получает доступ к данным о местоположении в фоновом режиме, подумайте, какую пользу оно приносит пользователям. Четко объясните им, зачем вашему приложению нужен этот доступ, чтобы повысить понимание и прозрачность.
Дополнительные рекомендации по доступу к местоположению, в том числе по геозонам, можно найти на странице Рекомендации по обеспечению конфиденциальности.
Используйте тип перехода DWELL, чтобы уменьшить количество оповещений
Если вы получаете слишком много оповещений, когда проезжаете мимо геозоны, используйте тип перехода
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 Network Location Provider).
Устранение неполадок с событием входа в геозону
Если геозоны не срабатывают, когда устройство входит в геозону (оповещение
GEOFENCE_TRANSITION_ENTER не срабатывает), сначала убедитесь, что ваши геозоны зарегистрированы правильно, как описано в этом руководстве.
Вот несколько возможных причин, по которым оповещения могут не работать должным образом:
- Внутри геозоны недоступно точное определение местоположения или геозона слишком мала. На большинстве устройств сервис геозоны использует только местоположение сети для активации геозоны. Этот подход используется потому, что определение местоположения по сети потребляет гораздо меньше энергии, быстрее предоставляет данные и, что самое важное, работает в помещениях.
На устройстве отключен Wi-Fi. Включение Wi-Fi может значительно повысить точность определения местоположения, поэтому, если Wi-Fi отключен, ваше приложение может никогда не получать оповещения о геозоне в зависимости от нескольких настроек, включая радиус геозоны, модель устройства или версию Android. Начиная с Android 4.3 (уровень API 18) мы добавили режим "Только сканирование Wi-Fi", который позволяет пользователям отключать Wi-Fi, но при этом получать точные данные о местоположении. Если оба режима отключены, рекомендуется предложить пользователю включить Wi-Fi или режим сканирования Wi-Fi и предоставить ярлык для этого. Используйте SettingsClient, чтобы убедиться, что системные настройки устройства правильно заданы для оптимального определения местоположения.
Примечание. Если ваше приложение предназначено для Android 10 (уровень API 29) или более поздней версии, вы не можете напрямую вызывать
WifiManager.setEnabled(), если только ваше приложение не является системным или контроллером правил для устройств (DPC). Вместо этого используйте панель настроек.- Внутри геозоны нет стабильного подключения к сети. Если надежное подключение к интернету отсутствует, оповещения могут не создаваться. Это связано с тем, что сервис геозоны зависит от поставщика сетевого местоположения, которому, в свою очередь, требуется подключение для передачи данных.
- Оповещения могут приходить с задержкой. Сервис геозоны не запрашивает местоположение постоянно, поэтому оповещения могут приходить с задержкой. Обычно задержка составляет менее двух минут, а если устройство перемещалось, то ещё меньше. Если действуют ограничения на доступ к данным о местоположении в фоновом режиме, задержка составляет в среднем 2–3 минуты. Если устройство долгое время оставалось неподвижным, задержка может увеличиться до шести минут.
Дополнительные ресурсы
Дополнительную информацию о геозонах можно найти в следующих материалах:
Образцы
Пример приложения для создания геозон и отслеживания пересечения их границ.