最后一次已知位置记录是多久以前?

17

我已获取到自己最后已知位置,但不知道该位置上次更新以来的时间长度。有什么方法可以找出该位置上次更新的时间长度吗?

LocationManager locationManager 
                        = (LocationManager) getSystemService(LOCATION_SERVICE);
Criteria c = new Criteria();
    c.setAccuracy(Criteria.ACCURACY_FINE);
    c.setAccuracy(Criteria.ACCURACY_COARSE);
    c.setAltitudeRequired(false);
    c.setBearingRequired(false);
    c.setCostAllowed(true);
    c.setPowerRequirement(Criteria.POWER_HIGH);
String provider = locationManager.getBestProvider(c, true);
Location location = locationManager.getLastKnownLocation(provider);

订阅位置更新并测量接收更新之间的时间差如何?当您获取位置更新时,Location实例允许您通过getTime()检索更新时间(请注意,它是在UTC中)。 - AgentKnopf
location.getTime();讲述了使用getElapsedRealtimeNanos()来计算定位修复的年龄并比较位置修复。博客上的示例使用了getTime()方法。 - kush
getElapsedRealtimeNanos()是推荐的方法,但仅适用于API 17(4.2)及以上版本,因此在撰写本文时,getTime是唯一可用的方法。 - JamesSugrue
4个回答

25

同时适用于API 17之前和之后的最佳选择:

public int age_minutes(Location last) {
    return age_ms(last) / (60*1000);
}

public long age_ms(Location last) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
        return age_ms_api_17(last);
    return age_ms_api_pre_17(last);
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private long age_ms_api_17(Location last) {
    return (SystemClock.elapsedRealtimeNanos() - last
            .getElapsedRealtimeNanos()) / 1000000;
}

private long age_ms_api_pre_17(Location last) {
    return System.currentTimeMillis() - last.getTime();
}

Pre 17并不是非常准确,但应该足以测试一个位置是否非常古老。

我认为这样应该可以:

if (age_minutes(lastLoc) < 5) {
   // fix is under 5 mins old, we'll use it

} else {
   // older than 5 mins, we'll ignore it and wait for new one

}

这种逻辑的常见用例是当应用程序刚启动时,我们需要知道是否必须等待新位置,还是可以在等待新位置的同时现在使用最新位置。


我认为这个解决方案有一个缺陷 - 想象一下,如果我将lastLocation存储在某个持久性存储中。假设我在上次手机重启后3小时得到了最后的位置更新。这个位置更新被存储在一个存储器中。然后我再次重启手机并调用“age_minutes(last)”。我会得到SystemClock = ~1min和lastLocation=3hours。"age_minutes"会返回true,这是不正确的。 - Michal
@Michal 的缺陷是认为你自己存储最后一个位置。始终按照问题中的行询问locationManagerproviderLocation location = locationManager.getLastKnownLocation(provider); - weston
@Michal 如果你不这样做并自己存储最后位置,那么除了引起你所说的错误之外,你也无法从其他应用程序请求位置的好处。 - weston
你说得对,我们应该始终使用Android提供的最后位置,这样对于这里提出的问题确实可以很好地工作。我一直在将其视为可测试的代码片段。 - Michal
虽然你不应该自己存储最后的位置,但如果你真的想要这么做,你可以添加一个检查,比如 age_minutes(lastLoc) > 0,这将解决 @Michal 在第一条评论中提到的情况。不是吗? - div
如果你不听从我的建议,坚持使用“位置”(Location),那么在API 17中,“age_minutes”将始终为“>0”。你不应该试图解决你没有的问题,只需询问位置管理器即可。 - weston

7

很抱歉重新提出这个问题,但我认为weston的答案是不正确的, 而Android文档至少是误导性的。

getTime()的时间基准是UTC,根据从GPS模块接收到的NMEA语句确定,而不是System.currentTimeMillis()。 GPS时间精确到纳秒(必须如此,因为电磁波每1ns行进30cm)。唯一的复杂之处在于,由于GPS闰秒(参见[1]),它可能会偏差1秒;这可能在每隔几年的几分钟内发生,假设GPS足够聪明,可以记住跨电源周期的UTC偏移)

另一方面,由于漂移或用户设置时间/日期不正确,System.currentTimeMillis()可能会偏差几秒/几分钟,甚至更多。

因此,在API 17之前,唯一真正的解决方案是定期接收位置更新,并每次记录基于SystemClock.elapsedRealtime()的自己的时间戳。

我刚试过了三星S4,请指出如果其他手机给出不同的结果。 我怀疑,不过。

[1] http://en.wikipedia.org/wiki/Global_Positioning_System#Leap_seconds


我认为你是正确的。只是偶然看到了这个。你应该将批评作为评论发布在我的答案上,否则我就看不到它。 - weston
如果我再得到一个赞,我就有足够的声望来发表评论 :)(这似乎是一个例外,因为我在回复自己的帖子) - Gerhard Wesp
你使用SystemClock.elapsedRealtime()获取重启时间间隔的方法是什么? - sb4
有人能否评论一下Location.getTime()的最大误差是多少? - sb4

5

Location.getTime()实际上不是找到最后已知位置的年龄的最佳方法。

从JavaDoc中可以看到:

返回自1970年1月1日以来的UTC时间,以毫秒为单位。

请注意,设备上的UTC时间不是单调的:它可能会不可预测地向前或向后跳跃。因此,在计算时间差时始终使用getElapsedRealtimeNanos。

要使用的两种方法是:

SystemClock.elapsedRealtimeNanos();
Location.getElapsedRealtimeNanos();

请注意,LocationManager.lastKnownLocation() 可能返回 null。

1
每个位置都有一个时间属性。使用 getTime() 获取它。将其与当前时间进行比较(计算差异)。这将给出“年龄”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接