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

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

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

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

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

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

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

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

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

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

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

Котлин

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 Services убит и перезапускается ограничением с надлежащим ресурсом системы.
  • Процесс определения местоположения дает сбой.

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

  • Устройство перезагружается. Приложение должно прослушать заполнительное действие устройства, а затем зарегистрировать необходимые геофические услуги.
  • Приложение удаляется и устанавливается заново.
  • Данные приложения будут удалены.
  • Данные Google Play Services очищены.
  • Приложение получило оповещение 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 минут).

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

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

Образцы

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