如何查看GPS接收器的当前状态?


90

如何查看GPS接收器的当前状态?我已经检查了LocationListener onStatusChanged方法,但是以某种方式似乎不起作用,或者只是错误的可能性。

基本上我只需要知道屏幕顶部的GPS图标是闪烁(没有实际修复)还是稳定(修复可用)即可。

Answers:


150

作为SpeedView:适用于Android的GPS车速表的开发人员,我必须尝试过所有可能解决此问题的方法,但结果都是相同的。让我们重申什么无效:

  1. 在Eclair和Froyo上没有调用onStatusChanged()。
  2. 当然,简单地计算所有可用卫星是没有用的。
  3. 检查是否有任何卫星对usedInFix()返回true也不是很有帮助。该系统显然丢失了此修复程序,但仍在报告其中仍使用了多个sat。

因此,这是我找到的唯一可行的解​​决方案,也是我在应用程序中实际使用的解决方案。假设我们有一个实现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 2010年

3
嗨,斯蒂芬,您能解释一下为什么 GPSFix起作用的原因。谢谢,尼克
nickfox 2010年

顺便说一句,我注意到在ICS中,他们增加了时间,直到系统开始报告GPS修复已丢失。至少在4.0.3中,它恰好是10秒。
soundmaven

效果很好,对于ICS,使用10秒钟而不是3-5秒钟也很好。幸运的是,修复不是我的应用程序的必要功能,但能够了解它是一件好事。谢谢。
汤姆(Tom)

1
但是,当mLastLocationMillis不是由GPS以外的其他来源设置时,这不会中断吗?该系统将声称它isGPSFix是正确的,但实际上这只是任何修复(可能是来自电话网络的修复)
Patrick

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 / AndroidRuntime:致命例外:主要过程:xxx.yyy.zzz.debug,PID:17344 java。 lang.SecurityException:权限拒绝:不允许从pid = 17344,uid = 10416发送广播android.location.GPS_FIX_CHANGE
无限制

19

不幸的是,新成员无法发表评论或投票,但是斯蒂芬·戴(Stephen Daye)的上述帖子是对我一直在寻求帮助的相同问题的完美解决方案。

对以下行进行了小的改动:

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和卫星信息,但我认为一个主意是看有多少颗卫星可用。如果低于3,则无法修复。如果更多,那么您应该有一个解决方法。

反复试验可能是确定Android报告卫星信息的频率以及每个GpsSatellite对象包含的信息的方式。


计算可用卫星数的问题是,即使您有5颗卫星在视线中,也不意味着总有可能进行修复。(您通过写“如果有更多内容,那么您应该有个解决办法”来指出它是正确的)
nr1 2010年

确实。尽管我不知道要构成一个修复程序需要什么信息,或者如何/是否可以从一个GpsSatellite对象中检索到这些信息。
Christopher Orr 2010年

另一个想法..您没有在问题中提到这一点,但是您是否尝试过LocationManager.requestLocationUpdates将时间和距离设置设置为0?那应该立即向您发送GPS修复。如果您什么都没有收到,那么您很可能没有解决办法。如果愿意,可以将其与上面的状态侦听器结合使用。
Christopher Orr 2010年

1
迭代“ sats”并检查GpsSatellite中的usedInFix()吗?
DeliriumTremens 2012年

8

在Windows Mobile上使用GPS几年后,我意识到“丢失” GPS修复的概念可能是主观的。为了简单地听GPS告诉您的内容,添加NMEAListener并解析该句子将告诉您该修复程序是否“有效”。参见http://www.gpsinformation.org/dale/nmea.htm#GGA。不幸的是,对于某些GPS,即使在“良好定位”区域的正常操作过程中,该值也会来回波动。

因此,另一种解决方案是将GPS位置的UTC时间与手机时间进行比较(转换为UTC)。如果它们相距一定的时差,则可以假设您丢失了GPS位置。


您是否愿意解释时间比较方法?在斯蒂芬·戴(Stephen Daye)的回答中,他比较了上次修复发生的时间与最新的GPS_EVENT_SATELLITE_STATUS事件之间的时间差...不确定测量的是什么。
Rokey Ge

7

在我的MSc项目上工作时遇到类似的问题,当设备停留在静态位置时,Daye的回答似乎错误地报告了“无法修复”。我已经对解决方案进行了一些修改,这似乎对我在静态位置上正常工作。我不怎么会影响电池,因为这不是我主要关心的问题,但是这是我通过在修复程序超时后重新请求位置更新来实现的。

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
只要您相信其他作者,就可以为自己的新作品或衍生作品获得自己的赞成票。这里的规则更关注的是人们people窃他人的答案,并在Stack Overflow的其他地方使用它们以不诚实地吸引代表。既然您提到您是基于其他答案,那么我认为这很好。
2017年

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 2012年


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()进行超时

自2.1开始,至少在N1上可靠运行。


3
您的解决方案在功能上过于复杂。但是,它做得很少。如果gps解析了我的位置后我去了地铁站,从而使gps丢了该怎么办?
Meandre

1

使用LocationManager后,您可以在getBestProvider()之后得到getLastKnownLocation()。这将为您提供一个Location对象,该对象具有以米为单位的getAccuracy()方法和以UTC毫秒为单位的getTime()方法

这是否给您足够的信息?

或者,也许您可​​以遍历LocationProviders并找出每个是否满足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 '19

1

如果在丢失修补程序的那一刻不需要更新,则可以以这种方式修改Stephen Daye的解决方案,即可以使用一种方法来检查该修补程序是否仍然存在。

因此,只要您需要一些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(),如果结果为true,则将变量“ lastKnownLocation”用作当前位置。


1

我知道这有点晚了。但是,如果您想知道是否有修复程序,为什么不使用NMEAListener。根据我的阅读,NMEAListener将为您提供NMEA句子,然后从中选择正确的句子。

RMC语句包含修复状态,该状态为A(表示正常)或V(表示警告)。GGA语句包含修复质量(0个无效,1个GPS或2个DGPS)

我无法为您提供任何Java代码,因为我只是从Android开始,但是我已经在C#中为Windows应用程序完成了GPS库,我希望将其与Xamarin一起使用。我只是碰到这个线程,因为我在寻找提供者信息。

到目前为止,我所读到的有关Location对象的内容对getAccuracy()和hasAccuracy()之类的方法并不满意。我习惯从NMEA语句中提取HDOP和VDOP值,以确定我的修正的准确性。进行修复很常见,但是HDOP不好,这意味着您的水平精度根本不是很好。例如,坐在办公桌前用外部蓝牙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平台都是一样的。我绝对可以在我的HTC Hero和Archos 5上看到对onStatusChanged的调用,但我并不感到惊讶,因为它并非在所有地方都有效。方案B:您使用另一个答案中显示的GPSStatusListener,然后简单地查看是否有任何卫星为usedInFix()返回true。根据我的经验,这与标准GPS图标的行为最为接近。(您是否尝试过找到实现该标准图标的源代码BTW?)
CvR 2010年

0

设置时间间隔来检查修复不是一个好选择。.我注意到,如果您不移动则不会调用onLocationChanged ..可以理解的原因是位置不变,这是可以理解的:)

更好的方法是例如:

  • 检查到最后收到的位置的时间间隔(以gpsStatusChanged为单位)
  • 如果该间隔大于15s,则设置变量:long_interval = true
  • 删除位置侦听器,然后再次添加它,通常,如果位置确实可用,您将获得更新的位置;否则,您将获得更新的位置-您可能丢失了位置
  • 在onLocationChanged中,您只需将long_interval设置为false。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.