Работа с GPS в Android

17 Мар

Работа с GPS

В данной статье я в кратце опишу основы работы с GPS в Android. Далее можно найти пример класса «обертки», который  я использовал в SMS Controller.

В первую очередь следует добавить в манифест приложение (файл AndroidManifest.xml) разрешение «android.permission.ACCESS_FINE_LOCATION».

Создадим класс LocationHelper и добавим в него несколько полей

public class LocationHelper {    
    //LocationManager - системный ресурс
    //через который будут получаться данные о местоположение
    LocationManager lm;
    //Экземпляр класса в котором будет вызван callback метод
    LocationResult locationResult;
    //время ожидания данных о текущем положение (в мс)
    final int waitTime = 120000;
    //Handler. Используеться вместо Timer для отложенного вызова метода
    private Handler mHandler = new Handler();

    ...
}

Единственным публичным методом, будет метод:

public boolean getLocation(Context context, LocationResult result)
    {
        ...

С помощью которого будет осуществляется запрос текущего местоположения. В метод необходимо передать Context работающей программы, и callback класс для возврата полученной позиции. Результат метода истина, в том случае, если запрос за текущим положением возможен. Рассмотрим реализацию метода:

Получаем доступ к системной службе, для работы с положением. Далее проверяем возможность запроса положения. Чтобы запрос был возможен, необходимо чтобы пользователь включил в настройках «Служб определения местоположения» соответствующие галочки. Если у приложения почему то не оказалось прав на доступ к службе LOCATION_SERVICE (напоминаю о необходимости добавить разрешение «android.permission.ACCESS_FINE_LOCATION» в манифест приложения),в этом месте возникнет исключение, но это не повод крашиться. Обработаем его, и вернем false.

      if(lm==null)
          lm=(LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
        
      //Если необходимые разрешения будут отсутствовать возникнет ошибка.
      try
      {
        gps_enabled=lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
      }
      catch(Exception ex){}
      try
      {
         network_enabled=lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
      }
      catch(Exception ex){}

      //Определение местоположения недоступно...
      if(!gps_enabled && !network_enabled)
          return false;

Собственно начинаем запрос положения. Подписываемся на обновления позиции, задав callback классы.

        //подписываемся на обновления источников гео данных
        if(gps_enabled)
            lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps); 
        if(network_enabled)
            lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);

А вдруг местоположение не удастся определить никогда?! Чтобы ограничить процесс поиска спутников по времени, используем Handler. Почему именно Handler а не Timer входящий в пространство имен java.util? Между ними есть одно ключевое различие: Timer вызывает в callback функцию в новом потоке, а Handler в том же потоке, из которого он был вызван. Это означает, что callback вызванный из Timer не будет иметь доступа к UI. Этот момент очень важен, так это может привести к различным ошибкам на этапе выполнения программы.

        //на случай если Handler уже связан с процессом GetLastLocation
        mHandler.removeCallbacks(GetLastLocation); //удаляем GetLastLocation
        //выполняем команду GetLastLocation 
        //с задержкой waitTime с помощью mHandler
        mHandler.postDelayed(GetLastLocation, waitTime);
        return true;
    }

Основной метод написан. Теперь необходимо реализовать классы для обработки событий, извещающих о изменение состояния. В методе getLocation используются 2 экземпляра LocationListener: locationListenerGps и locationListenerNetwork. Они имеют схожую реализацию. Рассмотрим locationListenerGps:

LocationListener locationListenerGps = new LocationListener() {
        public void onLocationChanged(Location location) {

Если вызывается эта функция, значит позиция телефона получена. Больше нет необходимости выполнять какие е то действия по таймеру. Поэтому в первую очередь останавливаем его. Затем отписываемся от обновления провайдеров гео данных.

            //отписываемся от отложенного выполнения(отключаем таймер)
            mHandler.removeCallbacks(GetLastLocation);
            //отписываемся от провайдеров гео данных
            lm.removeUpdates(this);
            lm.removeUpdates(locationListenerNetwork);

Наконец возвращаем результат в пользовательской код.

            //вызываем callback метод, извещающий о получение позиции
            locationResult.gotLocation(location);
        }
        ...
    };

На этом работа нашего класса успешно завершается, но если состояние не удается определить вызовется метод по таймеру(на самом деле не по таймеру, а c использованием отложенного вызова Handler). В нем мы выберем последнее известное положение телефона и вернем его.

private Runnable GetLastLocation = new Runnable() {
        @Override
        public void run() {
             ...

             //если удалось получить позицию через оба провайдера
             if(gps_loc!=null && net_loc!=null){
                 //выбираем тот, который последним вернул результат
                 if(gps_loc.getTime()>net_loc.getTime())  
                     locationResult.gotLocation(gps_loc);
                 else
                     locationResult.gotLocation(net_loc);
                 return;
             }

             ...
        }
    }

Все. Здесь работа нашего класса заканчивается окончательно и бесповоротно.
Для его использования достаточно создать экземпляр класса LocationManager, вызвать метод getLocation.

LocationHelper locatioHelper = new LocationHelper();
//начинаем запрашивать положение телефона
if(!locatioHelper.getLocation(mainServiceContext, locationResult))
	{
	        //пошел запрос. Скоро будет результат
                ...
	}
	else
        {
		//Что то не так... Запрос за положением не возможен.
                ...
        }

И реализовать callback куда вернется результат

public LocationResult locationResult = new LocationResult(){
		@Override
		public void gotLocation(Location location) {			
			if(location!=null)
			{
			     //делаем что с location				
			}else
			{
			     //определяли определяли... Да не определили
			}
		}
	};

Код написан. Теперь надо проверить его работу. Отлаживать GPS в Android очень просто, используя DDMS. Необходимо помнить только одну небольшую особенность, виртуальная машина с Android не поддерживает определение положения через мобильные сети. Можно подменять только показания GPS.

Конечно широта и долгота которые можно получить из объекта Location, мало о чем говорят пользователю, поэтому в своей программе я использую AsyncTask и API Яндекс.Карт, чтобы по координатам определить адрес. Но это тема для другой статьи.

Исходный код класса LocationHelper можно взять здесь: LocationHelper.java

Не жадничай! Делись!

Метки: ,

Отзывов (2)

  1. Очень познавательно,спасибо)

    • s.pas:

      Не за что :) Кстати в ходе дальнейшей разработки был выявлен один неприятный глюк. При первом запросе координат от вышек, возвращаются старые координаты с новым временем. У себя в программе я просто стал игнорировать первые данные полученные через вышки.

Фирма по перевозкам www.perevozka-i-pereezd.ru

Ваш отзыв

maybe_its_me

Developer and just a good guy