Как я могу проверить текущее состояние GPS-приемника?


90

Как я могу проверить текущее состояние GPS-приемника? Я уже проверилLocationListener onStatusChanged метод, но почему-то кажется, что он не работает, или это просто неправильная возможность.

В основном мне просто нужно знать, мигает ли значок GPS в верхней части экрана (фактического исправления нет) или постоянно (исправление доступно).

Ответы:


150

Как разработчик SpeedView: GPS-спидометр для Android, я, должно быть, перепробовал все возможные решения этой проблемы, и все с тем же отрицательным результатом. Повторим, что не работает:

  1. onStatusChanged () не вызывается в Eclair и Froyo.
  2. Просто подсчитывать все доступные спутники, конечно, бесполезно.
  3. Проверка того, возвращает ли какой-либо из спутников true для usedInFix (), также не очень полезна. Система явно теряет исправление, но продолжает сообщать, что в ней все еще используются несколько спутников.

Итак, вот единственное рабочее решение, которое я нашел, и то, которое я использую в своем приложении. Допустим, у нас есть этот простой класс, который реализует GpsStatus.Listener:

private class MyGPSListener implements GpsStatus.Listener {
    public void onGpsStatusChanged(int event) {
        switch (event) {
            case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
                if (mLastLocation != null)
                    isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < 3000;

                if (isGPSFix) { // A fix has been acquired.
                    // Do something.
                } else { // The fix has been lost.
                    // Do something.
                }

                break;
            case GpsStatus.GPS_EVENT_FIRST_FIX:
                // Do something.
                isGPSFix = true;

                break;
        }
    }
}

Хорошо, теперь в onLocationChanged () мы добавляем следующее:

@Override
public void onLocationChanged(Location location) {
    if (location == null) return;

    mLastLocationMillis = SystemClock.elapsedRealtime();

    // Do something.

    mLastLocation = location;
}

Вот и все. По сути, это строка, которая все делает:

isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < 3000;

Конечно, вы можете настроить значение в миллисекундах, но я бы посоветовал установить его примерно на 3-5 секунд.

Это действительно работает, и хотя я не смотрел исходный код, который рисует собственный значок GPS, это почти повторяет его поведение. Надеюсь, это кому-то поможет.


Привет, мне интересно, почему подсчет доступных спутников бесполезен, как в этом примере? Если количество найденных спутников 0 означает отсутствие связи, или я ошибаюсь? groups.google.com/group/android-developers/browse_thread/thread/…
per_jansson

3
Привет, Стивен, не могли бы вы объяснить, почему работает GPSFix. Спасибо, Ник
nickfox

Кстати, я заметил, что в ICS увеличили время, пока система не начнет сообщать, что исправление GPS потеряно. По крайней мере в 4.0.3 это ровно 10 секунд.
soundmaven

Отлично работает, и использование 10 секунд вместо 3-5 для ICS также работает достаточно хорошо. К счастью, исправление не является необходимой функцией моего приложения, но хорошо знать это. Благодарю.
Том

1
Но разве это не ломается, когда он mLastLocationMillisбыл установлен другим источником, кроме GPS? Система могла бы тогда утверждать, что isGPSFixэто правда, но на самом деле это просто любое исправление (возможно, одно из телефонной сети)
avalancha

25

Значок GPS меняет свое состояние в соответствии с полученными намерениями вещания. Вы можете изменить его состояние самостоятельно с помощью следующих примеров кода:

Сообщите, что GPS включен:

Intent intent = new Intent("android.location.GPS_ENABLED_CHANGE");
intent.putExtra("enabled", true);
sendBroadcast(intent);

Сообщите, что GPS получает исправления:

Intent intent = new Intent("android.location.GPS_FIX_CHANGE");
intent.putExtra("enabled", true);
sendBroadcast(intent);

Сообщите, что GPS больше не получает исправления:

Intent intent = new Intent("android.location.GPS_FIX_CHANGE");
intent.putExtra("enabled", false);
sendBroadcast(intent);

Сообщите, что GPS отключен:

Intent intent = new Intent("android.location.GPS_ENABLED_CHANGE");
intent.putExtra("enabled", false);
sendBroadcast(intent);

Пример кода для регистрации получателя в намерениях:

// MyReceiver must extend BroadcastReceiver
MyReceiver receiver = new MyReceiver();
IntentFilter filter = new IntentFilter("android.location.GPS_ENABLED_CHANGE");
filter.addAction("android.location.GPS_FIX_CHANGE");
registerReceiver(receiver, filter);

Получая эти намерения вещания, вы можете заметить изменения в статусе GPS. Однако вы получите уведомление только при изменении состояния. Таким образом, невозможно определить текущее состояние, используя эти намерения.


Я не знаю, как вы можете отправить эту трансляцию из своего приложения, потому что кажется, что это защищенная система, то есть только система может ее отправлять. Любое приложение, отправляющее это намерение, должно аварийно завершить работу.
JoxTraex

Шесть лет назад можно было отправить, когда я написал ответ. Судя по всему, с тех пор они добавили в систему некоторую защиту.
2016,

Я использовал эти намерения, чтобы узнать, когда другие приложения используют GPS. Я считаю, что начиная с Nougat, вы больше не можете даже слушать эти намерения, или они изменились! Я не хочу отслеживать местоположение с помощью собственного приложения. У кого-нибудь есть другие идеи?
Flyview

Да, у меня такой сбой: 05-14 10: 25: 24.113 17344-17344 / xxx.yyy.zzz.debug E / Android Время выполнения: НЕОБХОДИМО ИСКЛЮЧЕНИЕ: основной процесс: xxx.yyy.zzz.debug, PID: 17344 java. lang.SecurityException: Permission Denial: не разрешено отправлять широковещательную передачу android.location.GPS_FIX_CHANGE с pid = 17344, uid = 10416
unlimited101

19

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

небольшое изменение в следующей строке:

isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < 3000;

кому:

isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < (GPS_UPDATE_INTERVAL * 2);

в основном, поскольку я создаю медленную игру, и мой интервал обновления уже установлен на 5 секунд, как только сигнал gps отсутствует на 10+ секунд, это подходящее время, чтобы что-то запустить.

Ура, приятель, потратил около 10 часов, пытаясь решить это решение, прежде чем нашел ваш пост :)


14

Хорошо, давайте попробуем комбинацию всех ответов и обновлений на данный момент и сделаем что-то вроде этого:

Слушатель GPS может быть примерно таким:

GpsStatus.Listener listener = new GpsStatus.Listener() {
    void onGpsStatusChanged(int event) {
        if (event == GPS_EVENT_SATELLITE_STATUS) {
            GpsStatus status = mLocManager.getGpsStatus(null);
            Iterable<GpsSatellite> sats = status.getSatellites();
            // Check number of satellites in list to determine fix state
        }
    }
}

В API-интерфейсах немного неясно, когда и какая информация о GPS и спутниках предоставляется, но я думаю, что идея состояла бы в том, чтобы посмотреть, сколько спутников доступно. Если он ниже трех, то вы не можете исправить это. Если больше, то у вас должно быть исправление.

Метод проб и ошибок - это, вероятно, способ определить, как часто Android сообщает спутниковую информацию и какую информацию GpsSatelliteсодержит каждый объект.


Проблема с подсчетом доступных спутников состоит в том, что даже если у вас в поле зрения 5 спутников, это не означает, что исправление всегда возможно. (Вы правильно упомянули об этом, написав «Если больше, то у вас должно быть исправление»)
nr1

Конечно. Хотя я больше не знаю, какая информация требуется для исправления, и как / можно ли ее получить из GpsSatelliteобъекта.
Кристофер Орр

Еще одна мысль ... вы не упоминаете об этом в своем вопросе, но пробовали ли вы просто использовать LocationManager.requestLocationUpdatesс настройками времени и расстояния, установленными на 0? Это должно отправить вам исправления GPS, как только они появятся. Если вы ничего не получаете, то, скорее всего, у вас нет решения. Вы можете комбинировать это со слушателем статуса выше, если хотите.
Кристофер Орр

1
Может быть, итерация "sats" и проверка usedInFix () в GpsS satellite?
DeliriumTremens

8

После нескольких лет работы с GPS на Windows Mobile я понял, что концепция «потери» исправления GPS может быть субъективной. Чтобы просто послушать, что вам сообщает GPS, добавив NMEAListener и проанализировав предложение, вы узнаете, было ли исправление «действительным» или нет. См. Http://www.gpsinformation.org/dale/nmea.htm#GGA . К сожалению, у некоторых GPS это значение будет колебаться взад и вперед даже во время нормального режима работы в зоне «хорошего исправления».

Итак, другое решение - сравнить время в формате UTC местоположения GPS со временем телефона (преобразованным в UTC). Если между ними есть определенная разница во времени, можно предположить, что вы потеряли координаты GPS.


Не могли бы вы объяснить метод сравнения времени? В ответе Стивена Дэйя он сравнил разницу во времени между тем, когда произошло последнее исправление, и последним событием GPS_EVENT_SATELLITE_STATUS ... Не уверен, что он измеряет.
Rokey Ge 03

7

столкнуться с подобной проблемой во время работы над моим проектом MSc, кажется, что ответ Дайе ошибочно сообщил «нет исправлений», пока устройство остается в статическом месте. Я немного изменил решение, которое, похоже, отлично работает для меня в статическом месте. Я не знаю, как это повлияет на батарею, поскольку это не моя главная проблема, но вот как я сделал это, повторно запросив обновления местоположения, когда время исправления истекло.

private class MyGPSListener implements GpsStatus.Listener {
    public void onGpsStatusChanged(int event) {
        switch (event) {
        case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
            if (Global.getInstance().currentGPSLocation != null)
            {
                if((SystemClock.elapsedRealtime() - mLastLocationMillis) < 20000)
                {
                    if (!hasGPSFix) 
                        Log.i("GPS","Fix Acquired");
                    hasGPSFix = true;
                } 
                else
                {
                    if (hasGPSFix) 
                    {
                        Log.i("GPS","Fix Lost (expired)");
                        lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 10, locationListener);
                    }
                    hasGPSFix = false;
                }
            }
            break;
        case GpsStatus.GPS_EVENT_FIRST_FIX:
            Log.i("GPS", "First Fix/ Refix");
            hasGPSFix = true;
            break;
        case GpsStatus.GPS_EVENT_STARTED:
            Log.i("GPS", "Started!");
            break;
        case GpsStatus.GPS_EVENT_STOPPED:
            Log.i("GPS", "Stopped");
            break;
        }
    }
}

5

Что ж, объединение каждого рабочего подхода приведет к следующему (также имеющему отношение к устаревшим GpsStatus.Listener):

private GnssStatus.Callback mGnssStatusCallback;
@Deprecated private GpsStatus.Listener mStatusListener;
private LocationManager mLocationManager;

@Override
public void onCreate() {
    mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

    mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
    if (checkPermission()) {
       mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, GPS_UPDATE_INTERVAL, MIN_DISTANCE, this);
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        mGnssStatusCallback = new GnssStatus.Callback() {
            @Override
            public void onSatelliteStatusChanged(GnssStatus status) {
                satelliteStatusChanged();
            }

            @Override
            public void onFirstFix(int ttffMillis) {
                gpsFixAcquired();

            }
        };
        mLocationManager.registerGnssStatusCallback(mGnssStatusCallback);
    } else {
        mStatusListener = new GpsStatus.Listener() {
            @Override
            public void onGpsStatusChanged(int event) {
                switch (event) {
                    case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
                        satelliteStatusChanged();
                        break;
                    case GpsStatus.GPS_EVENT_FIRST_FIX:
                        // Do something.
                        gpsFixAcquired();
                        break;
                }
            }
        };
        mLocationManager.addGpsStatusListener(mStatusListener);
    }
}

private void gpsFixAcquired() {
    // Do something.
    isGPSFix = true;
}

private void satelliteStatusChanged() {
    if (mLastLocation != null)
        isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < (GPS_UPDATE_INTERVAL * 2);

    if (isGPSFix) { // A fix has been acquired.
        // Do something.
    } else { // The fix has been lost.
        // Do something.
    }
}

@Override
public void onLocationChanged(Location location) {
    if (location == null) return;

    mLastLocationMillis = SystemClock.elapsedRealtime();

    mLastLocation = location;
}

@Override
public void onStatusChanged(String s, int i, Bundle bundle) {

}

@Override
public void onProviderEnabled(String s) {

}

@Override
public void onProviderDisabled(String s) {

}

Примечание: этот ответ представляет собой комбинацию ответов выше.


1
Пока вы указываете других авторов, нет проблем с получением ваших собственных голосов за новую или производную работу. Правила здесь больше касаются людей, которые плагиатируют ответы других людей и используют их в других местах на Stack Overflow, чтобы нечестно привлечь репутацию. Поскольку вы упомянули, что опираетесь на другие ответы, я думаю, что это нормально.
Halfer

3

Если вам просто нужно знать, есть ли исправление, проверьте последнее известное местоположение, указанное приемником GPS, и проверьте значение .getTime (), чтобы узнать, сколько ему лет. Если это достаточно недавно (например, несколько секунд), у вас есть исправление.

   LocationManager lm = (LocationManager)context.getSystemService(LOCATION_SERVICE); 
   Location loc = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);

   // Get the time of the last fix
   long lastFixTimeMillis = loc.getTime(); 

... и, наконец, сравните это с текущей датой (в UTC!). Если это достаточно недавно, у вас есть исправление.

Я делаю это в своем приложении, и пока все хорошо.


+1 кажется стоит попробовать, если вы сразу захотите узнать, есть ли исправление.
AgentKnopf

2

Вы можете попробовать использовать LocationManager.addGpsStatusListener, чтобы получать обновления при изменении статуса GPS. Похоже, GPS_EVENT_STARTED и GPS_EVENT_STOPPED могут быть тем, что вы ищете.


3
Статусный слушатель в сочетании со GPS_EVENT_FIRST_FIXзвуками похож на более близкого.
Кристофер Орр

2

Я могу ошибаться, но кажется, что люди уходят от темы

мне просто нужно знать, мигает ли значок GPS в верхней части экрана (фактического исправления нет)

Это легко сделать с

LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
boolean gps_on = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);

Чтобы увидеть, есть ли у вас надежное решение, все становится немного сложнее:

public class whatever extends Activity {
    LocationManager lm;
    Location loc;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);        
        lm = (LocationManager) getSystemService(LOCATION_SERVICE);
        loc = null;
        request_updates();        
    }

    private void request_updates() {
        if (lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            // GPS is enabled on device so lets add a loopback for this locationmanager
            lm.requestLocationUpdates(LocationManager.GPS_PROVIDER,0, 0, locationListener);
        }      
    }

    LocationListener locationListener = new LocationListener() {
        public void onLocationChanged(Location location) {
            // Each time the location is changed we assign loc
            loc = location;
        }

         // Need these even if they do nothing. Can't remember why.
         public void onProviderDisabled(String arg0) {}
         public void onProviderEnabled(String provider) {}
         public void onStatusChanged(String provider, int status, Bundle extras) {}
    };

Теперь, когда вы хотите посмотреть, есть ли у вас исправление?

if (loc != null){
    // Our location has changed at least once
    blah.....
}

Если вы хотите нарядиться, вы всегда можете задать тайм-аут, используя System.currentTimeMillis () и loc.getTime ()

Работает надежно, по крайней мере, на N1 начиная с версии 2.1.


3
Ваше решение слишком сложно для того, что оно делает. Однако это очень мало. Что, если после того, как GPS определит мое местоположение, я пойду на станцию ​​метро, ​​что приведет к потере этого местоположения?
меандре

1

С помощью LocationManager вы можете получитьLastKnownLocation () после того, как получитеBestProvider (). Это дает вам объект Location, который имеет методы getAccuracy () в метрах и getTime () в миллисекундах UTC.

Это дает вам достаточно информации?

Или, возможно, вы могли бы перебрать LocationProvider и выяснить, соответствует ли каждый из них Criteria (ACCURACY_COARSE)


1

так много постов ...

GpsStatus.Listener gpsListener = new GpsStatus.Listener() {
                        public void onGpsStatusChanged(int event) {
                            if( event == GpsStatus.GPS_EVENT_FIRST_FIX){
                                showMessageDialog("GPS fixed");
                            }
                        }
                 };

добавление этого кода с помощью addGpsListener ... showMessageDialog ... просто показывает стандартное диалоговое окно со строкой

отлично справился с работой :) Большое спасибо: =) (простите за этот пост, пока не могу проголосовать)


Это только первое исправление. Что, если вы войдете в туннель?
Leos Literak 03

1

Если вам не нужно обновление в тот момент, когда исправление потеряно, вы можете изменить решение Стивена Дейя таким образом, чтобы у вас был метод, который проверяет наличие исправления.

Таким образом, вы можете просто проверить его, когда вам нужны данные GPS, и вам не нужен этот GpsStatus.Listener.

«Глобальные» переменные:

private Location lastKnownLocation;
private long lastKnownLocationTimeMillis = 0;
private boolean isGpsFix = false;

Это метод, который вызывается в onLocationChanged () для запоминания времени обновления и текущего местоположения. Кроме того, обновляется "isGpsFix":

private void handlePositionResults(Location location) {
        if(location == null) return;

        lastKnownLocation = location;
        lastKnownLocationTimeMillis = SystemClock.elapsedRealtime();

        checkGpsFix(); // optional
    }

Этот метод вызывается всякий раз, когда мне нужно узнать, есть ли исправление GPS:

private boolean checkGpsFix(){

    if (SystemClock.elapsedRealtime() - lastKnownLocationTimeMillis < 3000) {
        isGpsFix = true;

    } else {
        isGpsFix = false;
        lastKnownLocation = null;
    }
    return isGpsFix;
}

В своей реализации я сначала запускаю checkGpsFix (), и если результат верен, я использую переменную lastKnownLocation в качестве моей текущей позиции.


1

Я знаю, что это немного поздно. Однако почему бы не использовать NMEAListener, если вы хотите знать, есть ли у вас исправление. Из того, что я прочитал, NMEAListener выдаст вам предложения NMEA, и оттуда вы выберете правильное предложение.

Предложение RMC содержит статус исправления: A для OK или V для предупреждения. Предложение GGA содержит качество исправления (0 недействительно, 1 GPS или 2 DGPS)

Я не могу предложить вам никакого java-кода, поскольку я только начинаю работать с Android, но я сделал библиотеку GPS на C # для приложений Windows, которую я собираюсь использовать с Xamarin. Я наткнулся на эту ветку только потому, что искал информацию о провайдере.

Из того, что я читал до сих пор об объекте Location, мне не очень комфортно пользоваться такими методами, как getAccuracy () и hasAccuracy (). Я привык извлекать из предложений NMEA значения HDOP и VDOP, чтобы определить, насколько точны мои исправления. Довольно часто бывает исправление, но у вас паршивая HDOP, что означает, что ваша горизонтальная точность совсем не очень хорошая. Например, сидя за своим столом, отлаживая внешнее устройство Bluetooth GPS, упираясь в окно, вы, скорее всего, получите исправление, но очень плохие HDOP и VDOP. Поместите ваше устройство GPS в цветочный горшок на улице или что-то подобное, или добавьте внешнюю антенну к GPS, и вы сразу же получите хорошие значения HDOP и VDOP.


0

Возможно, это лучший вариант для создания TimerTask, который регулярно устанавливает для полученного Location определенное значение (null?). Если новое значение получено GPSListener, он обновит местоположение с текущими данными.

Думаю, это было бы рабочее решение.


0

Вы говорите, что уже пробовали onStatusChanged (), но у меня это работает.

Вот метод, который я использую (я позволяю самому классу обрабатывать onStatusChanged):

private void startLocationTracking() {
    final int updateTime = 2000; // ms
    final int updateDistance = 10; // meter
    final Criteria criteria = new Criteria();
    criteria.setCostAllowed(false);
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    final String p = locationManager.getBestProvider(criteria, true);
    locationManager.requestLocationUpdates(p, updateTime, updateDistance,
            this);
}

И я обрабатываю onStatusChanged следующим образом:

void onStatusChanged(final String provider, final int status,
        final Bundle extras) {
    switch (status) {
    case LocationProvider.OUT_OF_SERVICE:
        if (location == null || location.getProvider().equals(provider)) {
            statusString = "No Service";
            location = null;
        }
        break;
    case LocationProvider.TEMPORARILY_UNAVAILABLE:
        if (location == null || location.getProvider().equals(provider)) {
            statusString = "no fix";
        }
        break;
    case LocationProvider.AVAILABLE:
        statusString = "fix";
        break;
    }
}

Обратите внимание, что методы onProvider {Dis, En }abled () предназначены для включения и отключения GPS-отслеживания пользователем; не то, что вы ищете.


К сожалению, часто это не срабатывает. onLocationChanged () вызывается раз в секунду, но onStatusChanged вообще не вызывается. Иногда мне хочется просто запросить текущий статус вместо того, чтобы ждать уведомления, которое может не приходить в течение длительного времени или никогда.
Эдвард Фальк,

2
Правильно. После этого предыдущего ответа я сам кое-что узнал, и во-первых, не все платформы Android одинаковы. Я определенно вижу вызовы onStatusChanged как на моем HTC Hero, так и на моем Archos 5, но я не удивлен, что это работает не везде. Как насчет плана B: вы используете GPSStatusListener, который показан в другом ответе, и просто смотрите, возвращает ли какой-либо из спутников true для usedInFix (). По моему опыту, это ближе всего к поведению стандартного значка GPS. (Кстати, вы пытались найти источник, реализующий этот стандартный значок?)
CvR

0

Установка временного интервала для проверки исправления - не лучший выбор .. я заметил, что onLocationChanged не вызывается, если вы не двигаетесь .. что понятно, так как местоположение не меняется :)

Лучше было бы, например:

  • интервал проверки до последнего полученного местоположения (в gpsStatusChanged)
  • если этот интервал больше 15 секунд, установите переменную: long_interval = true
  • удалите прослушиватель местоположения и добавьте его снова, обычно тогда вы получаете обновленную позицию, если местоположение действительно доступно, если нет - вы, вероятно, потеряли местоположение
  • в onLocationChanged вы просто устанавливаете long_interval в false ..
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.