- PVSM.RU - https://www.pvsm.ru -
Некоторое время назад передо мной была поставлена задача по определению смены местоположения пользователя на карте. По результатам эксперимента в статье [1], для этих целей, по точности определения и энергоэффективности, прекрасно подходит Google Services Geofences.

Как работать с Geofences подробно рассмотрено в единственном русскоязычном примере по использованию Location APIs [2] в статье [3] на хабре, но с тех пор прошло уже 2 года, и информация сильно устарела.
Пример автора на github [4], к сожалению, даже не компилировался, поэтому я решил его завести под свежие версии библиотек. На мое удивление, изменений в API между com.google.android.gms:play-services:4.0.30 и com.google.android.gms:play-services:8.4.0 оказалось много! Собственно, о них дальше и пойдет речь в статье.
Для начала желательно ознакомиться с оригиналом [3].
В самой концепции ничего не поменялось, изменились только ответственные классы.
Так, вместо LocationClient имеем api.GoogleApiClient, callback'и из GooglePlayServicesClient также переехали в api.GoogleApiClient, вместо new LocationClient(this, this, this) появился удобный билдер:
new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
Немаловажным отличием является то, что добавлением и удалением геозон mGoogleApiClient занимается не напрямую, а через LocationServices.GeofencingApi.
GeofencingRequest build = ...
LocationServices.GeofencingApi.addGeofences(mGoogleApiClient, builder.build(), getPendingIntent())
.setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(@NonNull Status status) {
if (status.isSuccess()) {
String msg = "Geofences added: " + status.getStatusMessage();
Log.e("GEO", msg);
Toast.makeText(GeofencingService.this, msg, Toast.LENGTH_SHORT)
.show();
}
GeofencingService.this.onResult(status);
}
});
Также изменился первый параметр: вместо списка геозон передается GeofencingRequest, который можно получить через специальный билдер:
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
builder.addGeofences(mGeofenceListsToAdd);
GeofencingRequest build = builder.build();
Одна из возможностей нового билдера — управление поведением геозон в момент добавления. Например, в комментариях [6] к оригинальной статье спрашивали про возможность срабатывания триггера Exit geofence для случая, когда девайс находится снаружи зоны в момент ее установки. Теперь это можно сделать передав флаг GeofencingRequest.INITIAL_TRIGGER_EXIT через метод setInitialTrigger (int initialTrigger), по умолчанию флаги GeofencingRequest.INITIAL_TRIGGER_ENTER и GeofencingRequest.INITIAL_TRIGGER_DWELL. Флаги можно комбинировать между собой.
Кроме этого был убран последний параметр-callback, теперь вместо него LocationServices.GeofencingApi.addGeofences возвращает PendingResult, с помощью которого можно, блокируя поток, ожидать результат или получить ответ асинхронно, с помощью callback метода setResultCallback(..). В любом случае результатом будет статус операции добавленияудаления геозоны. Данный callback заменяет собой OnAddGeofencesResultListener или onRemoveGeofencesByRequestIdsResult из оригинальной статьи.
Удалить не нужные геозоны можно через методы:
LocationServices.GeofencingApi.removeGeofences(mGoogleApiClient, /*PendingIntent или список id геозон*/)
Если раньше для обработки результатов срабатывания триггеров на геозоне использовались статические методы из класса LocationClient, которые требовали в качестве параметра пришедший Intent, то сейчас этим занимается GeofencingEvent, который имеет одноименные методы для выполнения той же работы. Получить его можно следующим образом:
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
Создания самих геозон осталось без изменений и происходит через Geofence.Builder.
Еще одним из новшеств является то, что после срабатывания триггера геозона удаляется автоматически, таким образом нам не нужно больше убирать их самостоятельно!
Так же в код примера я добавил еще одну кнопку, которая ставит геозону с триггером на выход из нее.
Для тестирования я выбрал эмулятор Genymotion, но при попытке установить геозону LocationServices выдавал ошибку status code = 1000 (GEOFENCE_NOT_AVAILABLE). Решение этой проблемы нашлось на stackoverflow [7]
Если выставить в качестве условия срабатывания триггеры GeofencingRequest.INITIAL_TRIGGER_EXIT и GeofencingRequest.INITIAL_TRIGGER_ENTER или Geofence.GEOFENCE_TRANSITION_ENTER и Geofence.GEOFENCE_TRANSITION_EXIT, то сработает только одно условие из каждой пары, после чего зона будет удалена
Для работы с Rx можно использовать эту [8] библиотеку, тогда весь процесс по добавлениюудалению сводится к коду:
GeofencingRequest.Builder builder = new GeofencingRequest.Builder()
...
new ReactiveLocationProvider(context).
locationProvider.addGeofences(getPendingGeoIntent(),builder.build())
.subscribe(status -> {
if (status.isSuccess()) {}
};
Ссылки:
Автор: Vilkaman
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android-development/118704
Ссылки в тексте:
[1] статье: https://habrahabr.ru/post/275749/
[2] Location APIs: https://developer.android.com/google/play-services/location.html
[3] статье: https://habrahabr.ru/post/210162/
[4] github: https://github.com/Ne4istb/AndroidGeofenceTest
[5] github: https://github.com/MrVilkaman/AndroidGeofenceTest
[6] комментариях: https://habrahabr.ru/post/210162/#comment_8363535
[7] stackoverflow: http://stackoverflow.com/questions/19082482/error-adding-geofences-in-android-status-code-1000
[8] эту: https://github.com/mcharmas/Android-ReactiveLocation
[9] lockito: https://play.google.com/store/apps/details?id=fr.dvilleneuve.lockito&hl=ru
[10] 1: http://developer.android.com/intl/ru/training/location/geofencing.html
[11] 2: https://developers.google.com/android/reference/com/google/android/gms/location/Geofence.Builder#public-methods
[12] Источник: https://habrahabr.ru/post/282129/
Нажмите здесь для печати.