Создание и мониторинг геозон

Геозенс сочетает в себе осведомленность о текущем местоположении пользователя и осведомленность о близости пользователя к местам, которые могут представлять интерес. Чтобы отметить интересующее вас место, вы указываете его широту и долготу. Чтобы настроить близость местоположения, вы добавляете радиус. Широта, долгота и радиус определяют геозону, создавая круглую область или забор вокруг интересующего местоположения.

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

В этом уроке показано, как добавлять и удалять геозоны, а затем прослушивать переходы геозон с помощью BroadcastReceiver .

Примечание. На устройствах Wear API-интерфейсы Geofencing не обеспечивают эффективного использования энергии. Мы не рекомендуем использовать эти API на Wear. Дополнительные сведения см. в разделе «Экономия энергии и заряда аккумулятора» .

Настройка мониторинга геозон

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

Дополнительную информацию см. в руководстве о том, как запросить разрешения на определение местоположения .

Если вы хотите использовать BroadcastReceiver для прослушивания переходов геозон, добавьте элемент, определяющий имя службы. Этот элемент должен быть дочерним элементом <application> :

<application
   android:allowBackup="true">
   ...
   <receiver android:name=".GeofenceBroadcastReceiver"/>
<application/>

Чтобы получить доступ к API местоположения, вам необходимо создать экземпляр клиента Geofencing. Чтобы узнать, как подключить клиента:

Котлин

lateinit var geofencingClient: GeofencingClient

override fun onCreate(savedInstanceState: Bundle?) {
    // ...
    geofencingClient = LocationServices.getGeofencingClient(this)
}

Ява

private GeofencingClient geofencingClient;

@Override
public void onCreate(Bundle savedInstanceState) {
    // ...
    geofencingClient = LocationServices.getGeofencingClient(this);
}

Создание и добавление геозон

Вашему приложению необходимо создавать и добавлять геозоны, используя класс построения 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())

Ява

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()
}

Ява

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 {
        val intent = Intent(this, GeofenceBroadcastReceiver::class.java)
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
        // addGeofences() and removeGeofences().
        PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
    }
}

Ява

public class MainActivity extends AppCompatActivity {

    // ...

    private PendingIntent getGeofencePendingIntent() {
        // Reuse the PendingIntent if we already have it.
        if (geofencePendingIntent != null) {
            return geofencePendingIntent;
        }
        Intent intent = new Intent(this, GeofenceBroadcastReceiver.class);
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
        // calling addGeofences() and removeGeofences().
        geofencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.
                FLAG_UPDATE_CURRENT);
        return geofencePendingIntent;
    }

Добавить геозоны

Чтобы добавить геозоны, используйте метод GeofencingClient.addGeofences() . Предоставьте объект GeofencingRequest и PendingIntent . Следующий фрагмент демонстрирует обработку результатов:

Котлин

geofencingClient?.addGeofences(getGeofencingRequest(), geofencePendingIntent)?.run {
    addOnSuccessListener {
        // Geofences added
        // ...
    }
    addOnFailureListener {
        // Failed to add geofences
        // ...
    }
}

Ява

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 , который отправляет уведомление при переходе геозоны. Когда пользователь нажимает на уведомление, появляется основное действие приложения:

Котлин

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))
        }
    }
}

Ява

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
        // ...
    }
}

Ява

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, если оба они отключены. Используйте SettingsClient, чтобы убедиться, что системные настройки устройства правильно настроены для оптимального определения местоположения.

    Примечание. Если ваше приложение предназначено для Android 10 (уровень API 29) или выше, вы не можете вызывать WifiManager.setEnabled() напрямую, если только ваше приложение не является системным приложением или контроллером политики устройства (DPC) . Вместо этого используйте панель настроек .

  • Внутри вашей геозоны нет надежного сетевого подключения. Если нет надежного соединения для передачи данных, оповещения могут не генерироваться. Это связано с тем, что служба геозон зависит от поставщика сетевого местоположения, который, в свою очередь, требует подключения для передачи данных.
  • Оповещения могут быть запоздалыми. Служба геозон не запрашивает постоянное местоположение, поэтому при получении оповещений ожидайте некоторую задержку. Обычно задержка составляет менее 2 минут, а при движении устройства даже меньше. Если действуют ограничения фонового местоположения , задержка составляет в среднем около 2–3 минут. Если устройство находилось в неподвижном состоянии в течение значительного периода времени, задержка может увеличиться (до 6 минут).

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

Чтобы узнать больше о геозонах, просмотрите следующие материалы:

Образцы

Пример приложения для создания и мониторинга геозон.