Як я можу перевірити поточний стан приймача GPS?


90

Як я можу перевірити поточний стан приймача GPS? Я вже перевірив LocationListener onStatusChangedметод, але якимось чином здається, що він не працює, або просто неправильна можливість.

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

Відповіді:


150

Як розробник SpeedView: GPS-спідометра для Android, я, мабуть, спробував усі можливі способи вирішення цієї проблеми, все з однаковим негативним результатом. Давайте повторимо, що не працює:

  1. onStatusChanged () не викликає Еклера та Фройо.
  2. Просто підраховувати всі доступні супутники, звичайно, марно.
  3. Перевірка того, чи повертається якийсь із супутників істинним для 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це правда, але насправді це будь-яке виправлення (можливо, одне з телефонної мережі)
лавина

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. Я вважаю, починаючи з Нуга, ти навіть не можеш більше слухати ці наміри, це або вони змінилися! Я не хочу слухати місцезнаходження за допомогою власного додатка. У когось є інші ідеї?
Flyview

Так, у мене аварія: 05-14 10: 25: 24.113 17344-17344 / xxx.yyy.zzz.debug E / Android Час роботи: ФАТАЛЬНЕ ВИНЯТКЕ: основний процес: xxx.yyy.zzz.debug, PID: 17344 java. lang.SecurityException: Відмова в дозволі: не дозволено надсилати трансляцію android.location.GPS_FIX_CHANGE від pid = 17344, uid = 10416
необмежено

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 супутників, це не означає, що виправлення можливо завжди. (Ви згадали це правильно, написавши "Якщо це більше, то вам слід виправити
ситуацію

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

Ще одна думка .. Ви не згадуєте про це у своєму питанні, але чи пробували ви просто використовувати LocationManager.requestLocationUpdatesпараметри часу та відстані, встановлені на 0? Це повинно надсилати вам виправлення GPS, як тільки вони трапляються. Якщо ви нічого не отримуєте, то, швидше за все, у вас немає виправлення. Якщо хочете, ви можете поєднати це із прослуховувачем стану вище.
Крістофер Орр

1
Ітерація "sats" та перевірка usedInFix () у GpsSatellite, можливо?
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

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, щоб нечесно залучити представників. Оскільки ви вже згадали, що базуєтесь на інших відповідях, я вважаю, що це нормально.
половинка

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

Можливо, я помиляюся, але, здається, люди, здається, відходять від теми

мені просто потрібно знати, чи блимає значок 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 вирішить моє місцезнаходження, я перейду до станції метро, ​​завдяки чому GPS втратить виправлення?
meandre

1

За допомогою LocationManager ви можете отриматиLastKnownLocation () після отриманняBestProvider (). Це дає вам об'єкт Location, який має методи getAccuracy () у метрах та getTime () у UTC мілісекундах

Це дає вам достатньо інформації?

Або, можливо, ви могли б переглянути ідентифікатори LocationProviders і з'ясувати, чи кожен відповідає критеріям (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 ... просто показує стандартне діалогове вікно із рядком

зробив роботу ідеально для мене :) велике спасибі: =) (вибачте за цей пост, ще не в змозі проголосувати)


Це лише перше виправлення. Що робити, якщо ви увійдете в тунель?
Леос Літерак

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

Ви говорите, що вже пробувалиStatusChanged (), але це працює для мене.

Ось метод, який я використовую (я дозволяю самому класу обробляти 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 взагалі не викликається. Іноді я бажаю, щоб я міг просто запитати поточний стан, а не чекати сповіщення, яке може не надходити довго чи взагалі.
Edward Falk

2
Правильно. Я дізнався кілька речей сам після цієї попередньої відповіді, і одна з них полягає в тому, що не всі платформи Android створені однаково. Я точно бачу дзвінки onStatusChanged як на своєму HTC Hero, так і на своєму Archos 5, але я не здивований, що це працює не скрізь. Як щодо плану Б: ви використовуєте GPSStatusListener, який показано в іншій відповіді, і просто перевіряєте, чи повертається якийсь із супутників true для usedInFix (). За моїм досвідом, це найближче до поведінки стандартної піктограми GPS. (Ви пробували знайти джерело, яке реалізує цю стандартну піктограму,
ДОТВЕР

0

Встановлення часового інтервалу для перевірки виправлення - невдалий вибір .. я помітив, що onLocationChanged не викликається, якщо ви не рухаєтесь .. що зрозуміло, оскільки місце розташування не змінюється :)

Кращий спосіб буде наприклад:

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