- Tracking Highly Accurate Location in Android — (Vol.1)
- Make a project
- LocationService class
- Permissions
- Bind and Connect LocationService instance
- LocationManager
- Criteria
- Criteria | Android Developers
- Minimum Time and Minimum Distance
- LocationListener
- GPSStatus.Listener
- Run the app
- Глубокое погружение в определение местоположения
- Покажи мне код!
- Что оно делает на самом деле?
- Теперь у вас есть код, давайте подробнее рассмотрим его
- Свежесть — значит никогда не ждать
- Используйте Intent’ы для получения обновлений местоположения
- Получение данных во время автономной работы
- Оптимизация времени работы аккумулятора: умные сервисы и использование состояния устройства для переключения ресиверов
- Мониторинг состояния аккумулятора для уменьшения функциональности и экономии энергии
Tracking Highly Accurate Location in Android — (Vol.1)
Tracking a user’s location is the core feature of apps such as Nike+, Pokemon Go, and Uber.
I’ve been making a running app from the time of iPhone3.1, and it took a lot of tries and errors to get accurate location information under various environmental condition. As Pokemon Go is getting the people’s attention back to location technology nowadays, I’ll share basic and practical knowledge of location tracking on Android OS.
Make a project
Make a new Android Studio project and make an Empty Activity.
LocationService class
Our aim is tracking user’s location even while the user puts the app in the background.
We use a Service object to do background location tracking.
Let’s start from making a subclass of Service class. We name it as “LocationService” in this project.
Since this LocationService class is not going be used by other apps, uncheck “Exported“ flag.
Now, Android Studio has added a LocationService class to the Manifest.xml file as blow. If you don’t see the class, add the same XML element as below to your Manifest file.
Permissions
In the Manifest file, let us add a few more lines of code.
Since we use the location service, we need to add proper permissions. (ACCESS_MOCK_LOCATION is to use dummy GPS location fed from Android emulators, thus you can omit when you release your app.)
Bind and Connect LocationService instance
- Start a LocationService within onCreate() of MainActivity.
- Bind the service to the MainActivity with a ServiceConnection object which is going to become the listener of binding events between the MainActivity and the LocationService.
- Within onServiceConnected() method, obtain the LocationService instance and store it to locationService.
- Also implement onServiceDisconnected() method. In this method, assign NULL to locationService since the LocationService object is going to be released.
Now we should get a reference to LocationService object.
LocationManager
Next step is to initialize a LocationManager object in the LocationService class’s implementation.
Create startUpdatingLocation method in LocationService, and within it, initialize a LocationManager object.
It’s pretty easy.
Criteria
After instantiating a LocationManager object, you need to setup various parameters. It is a bit tricky.
We must set Criteria properly.
After instantiate a Criteria object,
- Set the accuracy level to ACCURACY_FINE (for better location)
- Set the highest power (to get best GPS signal)
- Don’t request the altitude.
- Set speedRequired to false since we don’t obtain speed value from locationManger so .
- Set CostAllowed flag to true. This flag enables LocationManager to exchange “data packet” with 3G/4G network base stations in order to get better location. This additional data packet exchange may increase the cost for the user’s monthly or temporary data plan. If you don’t want to take that risk, you can set it to false or may create an UI to ask users to choose. Since we are interested in getting super high accuracy location information, we set it to true.
- Set BearingRequired to false. “Bearing” in this context is the direction the device is pointing at/moving in. We don’t use this information this time.
- Set HorizontalAccuracy and VerticalAccuracy to HIGH.
Below is the Criteria class reference. Check it if you are interested in other options for these parameters.
Criteria | Android Developers
Then we are going to call requestLocationUpdates with the minimum duration of 1000 milliseconds and the minimum distance of 1 meter, and the criteria we’ve created.
(gpsFreqInMillis is minimumTime, and gpsFreqInDistance is minimumDistance in the code below.)
Minimum Time and Minimum Distance
The role of minimum duration and distance is a type of filter to avoid your location callback functions from being called too frequent.
Minimum time of 1000 milli seconds means, after getting a location, you are going to get next location from the LocationManager roughly after 1000 milli seconds.
Minimum distance of 1 meter means, after getting a location, you are getting next location after you have moved roughly 1 meter.
These two filters are combined inside the LocationManager in the way you are going to get a location after “one of the” two conditions is filled. This means these two conditions are “OR”ed.
For example, if you are standing up and not moving, new location is obtained after 1 second even though you’ve not moved 1 meter yet.
On the other hand if you are running fast, you are getting a new location after you’ve moved 1 meter even though you’ve moved 1 meter within 0.5 seconds.
Change these values for your purpose.
If you are making an app like Uber, location updates can be less frequent. You may want to set the values to 10 seconds and 10 meters.
and LocationListner (by the 4th parameter of requestLocationUpdates).
LocationListener
Information about the location provider is given via LocationListener interface. We can catch events whether which location provider — Wifi or GPS is currently available. You’ll probably want to update your UI based on this information.
We already registered LocationService as the location listener by passing “this” to the 4th parameter of requestLocationUpdates in the code above.
GPSStatus.Listener
GPSStatusListener receives events as below from the lower level GPS engine.
- GPS_EVENT_STARTED
- GPS_EVENT_STOPPED
- GPS_EVENT_FIRST_FIX
- GPS_EVENT_SATELLITE_STATUS
My sample code doesn’t use these information, but you can get satellite status information by calling getGpsStatus on the LocationManager object.
To register your class as a GPSStatusListener, call LocationManager’s addGpsStatusListener.
Run the app
Run the app on Android Emulator.
Open Extended controls by clicking “…” on the menu bar of the emulator.
Here you can send individual GPS data to the emulator.
Here is Extended controls. You can set latitude and longitude values manually and click send button to send a location data to the app.
Or on the bottom, you can click “LOAD GPX/XML” button to load a GPX or KML file. The list of gps data loaded from the GPS or KML file is going to be shown in the table. By clicking the playback button, you can start feeding the location data to the app.
This is pretty convenient when you want to simulate your past run with the emulator.
(The sample projects has a file named “Half_Marathon.gpx” under the top directory. )
On Android Studio’s Logcat console, you will see the value of GPS data received in onLocationChanged().
You can switch to the commit “ faa85d2f5cacd94f4d9ff005336680f3f9b27891” to see code which only includes what has been explained until here.
In the next post, I will explain how to show the path of GPS waypoints, the user’s current location, and accuracy indicator (a surrounding circle which indicates how accurate the current location information is).
Even when I’m making a location tracking app without map, I always use a map for debugging and performance measurement.
So I’ll explain about the map in the next post.
Источник
Глубокое погружение в определение местоположения
Этот пост является переводом топика из блога android-developers. Далее повествование ведется от Рето Майера, автора книги Professional Android 2 Application Development. Он пишет о том, как можно улучшить приложения, использующие местоположение, в смысле кэширования результатов, скорости работы и так далее.
Без разницы, ищете ли вы место, где бы поесть, или ближайшее место велосипедов Boris Bike, всегда есть задержка при получении данных местоположения от GPS и заполнении абстрактного списка результатов в вакууме. Когда вы находитесь в месте, где хотели бы получить контекстную информацию, то часто вы сталкиваетесь с отсутствием подключения к данным.
Вместо того, чтобы грозить кулаком в небо, я написал open-source приложение, которое включает в себя советы и рекомендации по сокращению времени между открытием приложения и просмотром актуальной информации о близлежащих местах, вкупе с разумным обеспечением offline режима работы. И всё это, сохраняя использование аккумулятора на возможном минимуме.
Покажи мне код!
Вы можете проверить мой проект Android Protips for Location. Не забудьте прочесть Readme.txt, чтобы успешно скомпилировать и запустить приложение.
Что оно делает на самом деле?
Оно использует Google Places API для реализации базовой функциональности приложений, которые которые используют местоположение для определения списка близлежащих достопримечательностей (точек на карте), позволяют просмотреть их детали, а также проверить или оценить.
Код реализует многие из лучших практик, о которых я подробно рассказал на своей сессии на Google I/O 2011, Android Protips: Advanced Topics for Expert Android Developers (видео). В том числе использование Intent’ов для получения обновлений о местоположении, используя Passive Location Provider, наблюдение за состоянием устройства для изменения частоты обновлений, переключение Receiver’ов во время исполнения приложения, и также используя Cursor Loader.
Приложение написано для Honeycomb, но также может работать и на версия 1.6 и выше.
Теперь у вас есть код, давайте подробнее рассмотрим его
Мой главный приоритет — свежесть: минимизация задержек между открытием приложения и возможностью проверить нужные места, и в то же время минимизировать использование аккумулятора.
Требования:
- Текущее местоположение должно находиться так быстро, как это возможно
- Список мест должен обновляться при изменение местоположения
- Список близлежащих мест должен быть доступен (в деталях) в автономном режиме
- Отметки также должны быть доступны в автономном режиме
- Данные о местоположении и другие данные пользователя должны быть правильно обработаны (см. более ранний пост с лучшими практиками )
Свежесть — значит никогда не ждать
Можно значительно сократить время ожидания получения первого приближения к местоположению, путём выбора последнего известного из Location Manager’a каждый раз, когда приложение переходит в активный режим.
В этом примере из GingerbreadLastLocationFinder, мы перебираем все провайдеры местоположения на устройстве, даже те, которые на данный момент недоступны, чтобы найти наиболее точное последнее местоположение.
Если есть координаты одного или более местоположений, то выбирается самое точное. Иначе, просто возвращается самый недавний результат.
Во втором случае (когда определено, что последнее обновление местоположения недостаточно недавнее), значение так или иначе возвращается, но мы запрашиваем одно обновление местоположения, используя самый быстрый провайдер.
К сожалению, мы не можем определить наибыстрейших провайдеров, но, на практике, понятно, что определение местоположения при помощи сети возвращает результаты быстрее.
Отметим также, что этот фрагмент кода показывает GingerbreadLastLocationFinder, который использует метод requestSingleUpdate для получения единоразового обновления местоположения. Эта функциональность не была доступна до Gingerbread — проверьте LegacyLastLocationFinder, посмотрите как я реализовал такую же функциональность для более ранних версий Android.
singleUpdateReceiver передает полученное обновление назад к вызывающему классу через слушателя Location Listener.
Используйте Intent’ы для получения обновлений местоположения
Получив наиболее точную/своевременную оценку текущей позиции, мы также хотим получать её обновления.
Класс PlacesConstants включает в себя набор значений, которые определяют частоту обновления местоположения. Настройте их, чтобы убедиться, что обновления приходят так часто, как это требуется.
Следующий шаг — запросить обновление местоположения от Location Manager. В следующем фрагменте кода, взятом из GingerbreadLocationUpdateRequester мы можем передать критерии, используемые для определения — какой Location Manager будет запрашивать обновления напрямую в вызове метода requestLocationUpdates.
Обратите внимание, что мы передаём Pending Intent, а не Location Listener.
Вообще, я предпочитаю использовать Location Listener’ов, потому что они предоставляют гибкость в регистрации приёмников в нескольких Activity или Сервисах, или напрямую в манифесте.
В этом приложении, новое местоположение означает обновленный список близлежащих мест. Это происходит через сервисы, которые отправляют запросы к серверу и обновляют Контент-провайдер, который заполняет список мест.
Так как изменение местоположения напрямую не обновляет UI, то имеет смысл создавать и регистрировать связанный LocationChangedReceiver в манифесте, а не в Activity.
LocationChangedReceiver извлекает местоположение из каждого обновления и запускает сервис PlaceUpdateService, чтобы обновить базу близлежащих мест.
Получение данных во время автономной работы
Чтобы добавить поддержку режима offline, мы начнём с кэширования результатов поиска по PlacesContentProvider и PlaceDetailsContentProvider.
Также, при определенный обстоятельствах нужно проводить выборку сведений о местоположении. Этот фрагмент кода из сервиса PlacesUpdateService демонстрирует, как предварительная выборка включается для ограниченного количества мест.
Обратите внимание на то, что предварительная выборка данных потенциально выключается при низком заряде аккумулятора.
Похожая техника используется для реализации offline отметок.
Оптимизация времени работы аккумулятора: умные сервисы и использование состояния устройства для переключения ресиверов
Нет смысла запускать сервис обновления, когда устройство находится в автономном режиме. Сервис PlaceUpdateService проверяет возможность соединения, прежде чем пытаться получить обновление.
Если соединения нет, то ActiveLocationChangedReceiver и PassiveLocationChangedReceiver выключаются, а ConnectivityChangedReceiver включается.
ConnectivityChangedReceiver прослушивает все изменения сетевого соединения. Когда производится новое подключение, то он просто выключает себя и включает слушателей местоположения.
Мониторинг состояния аккумулятора для уменьшения функциональности и экономии энергии
Когда телефон на последних 15%, то большинство приложений не работает, для того чтобы сохранить заряд. Мы можем зарегистрировать ресиверов в манифесте, чтобы выдавать предупреждение, когда устройство входит или выходит из состояния низкой батареи.
Этот фрагмент из PowerStateChangedReceiver выключает PassiveLocationChangedReceiver всякий раз, когда устройство переходит в состояние низкого аккумулятора и включает его, когда заряд в порядке.
Можно расширить эту логику, отключив все упреждающие выборки или уменьшив частоту обновления в условиях низкого заряда аккумулятора.
Источник