关于地理位置:获取用户在Android中的位置的好方法

Good way of getting the user's location in Android

问题:

尽快将用户的当前位置置于阈值内,同时节省电池电量。

为什么问题是一个问题:

首先,android有两个提供商;网络和GPS。有时网络更好,有时GPS更好。

"更好"是指速度与准确度之比。
如果我几乎可以立即获得位置并且不打开GPS,我愿意牺牲几米精度。

其次,如果您要求更新位置更改,则在当前位置稳定时不会发送任何内容。

谷歌有一个确定"最佳"位置的例子:http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
但我认为它不应该接近它应该/可能的好。

我有点困惑为什么谷歌没有标准化的位置API,开发者不应该关心位置来自哪里,你应该只指定你想要的东西,手机应该为你选择。

我需要帮助的是:

我需要找到一个很好的方法来确定"最佳"位置,也许是一些启发式或者可能通过某些第三方库。

这并不意味着确定最好的提供商!
我可能会使用所有提供商并选择其中最好的。

应用程序的背景:

该应用程序将以固定间隔收集用户的位置(假设每10分钟左右)并将其发送到服务器。
该应用程序应尽可能节省电池,并且位置应具有X(50-100?)米的精度。

目标是以后能够在地图上绘制白天用户的路径,因此我需要足够的准确性。

其他:

您认为对于期望和接受的准确度的合理价值是什么?
我一直在使用100米接受,并且根据需要使用30米,这要问多少?
我希望以后能够在地图上绘制用户的路径。
期望100米,接受500米更好吗?

此外,现在我每次更新GPS的时间最长为60秒,如果你在室内的准确度可能超过200米,这个位置太短了吗?

这是我目前的代码,感谢任何反馈(除了缺少错误检查,这是TODO):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
protected void runTask() {
    final LocationManager locationManager = (LocationManager) context
            .getSystemService(Context.LOCATION_SERVICE);
    updateBestLocation(locationManager
            .getLastKnownLocation(LocationManager.GPS_PROVIDER));
    updateBestLocation(locationManager
            .getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
    if (getLocationQuality(bestLocation) != LocationQuality.GOOD) {
        Looper.prepare();
        setLooper(Looper.myLooper());
        // Define a listener that responds to location updates
        LocationListener locationListener = new LocationListener() {

            public void onLocationChanged(Location location) {
                updateBestLocation(location);
                if (getLocationQuality(bestLocation) != LocationQuality.GOOD)
                    return;
                // We're done
                Looper l = getLooper();
                if (l != null) l.quit();
            }

            public void onProviderEnabled(String provider) {}

            public void onProviderDisabled(String provider) {}

            public void onStatusChanged(String provider, int status,
                    Bundle extras) {
                // TODO Auto-generated method stub
                Log.i("LocationCollector","Fail");
                Looper l = getLooper();
                if (l != null) l.quit();
            }
        };
        // Register the listener with the Location Manager to receive
        // location updates
        locationManager.requestLocationUpdates(
                LocationManager.GPS_PROVIDER, 1000, 1, locationListener,
                Looper.myLooper());
        locationManager.requestLocationUpdates(
                LocationManager.NETWORK_PROVIDER, 1000, 1,
                locationListener, Looper.myLooper());
        Timer t = new Timer();
        t.schedule(new TimerTask() {

            @Override
            public void run() {
                Looper l = getLooper();
                if (l != null) l.quit();
                // Log.i("LocationCollector",
                //"Stopping collector due to timeout");
            }
        }, MAX_POLLING_TIME);
        Looper.loop();
        t.cancel();
        locationManager.removeUpdates(locationListener);
        setLooper(null);
    }
    if (getLocationQuality(bestLocation) != LocationQuality.BAD)
        sendUpdate(locationToString(bestLocation));
    else Log.w("LocationCollector","Failed to get a location");
}

private enum LocationQuality {
    BAD, ACCEPTED, GOOD;

    public String toString() {
        if (this == GOOD) return"Good";
        else if (this == ACCEPTED) return"Accepted";
        else return"Bad";
    }
}

private LocationQuality getLocationQuality(Location location) {
    if (location == null) return LocationQuality.BAD;
    if (!location.hasAccuracy()) return LocationQuality.BAD;
    long currentTime = System.currentTimeMillis();
    if (currentTime - location.getTime() < MAX_AGE
            && location.getAccuracy() <= GOOD_ACCURACY)
        return LocationQuality.GOOD;
    if (location.getAccuracy() <= ACCEPTED_ACCURACY)
        return LocationQuality.ACCEPTED;
    return LocationQuality.BAD;
}

private synchronized void updateBestLocation(Location location) {
    bestLocation = getBestLocation(location, bestLocation);
}

// Pretty much an unmodified version of googles example
protected Location getBestLocation(Location location,
        Location currentBestLocation) {
    if (currentBestLocation == null) {
        // A new location is always better than no location
        return location;
    }
    if (location == null) return currentBestLocation;
    // Check whether the new location fix is newer or older
    long timeDelta = location.getTime() - currentBestLocation.getTime();
    boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
    boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
    boolean isNewer = timeDelta > 0;
    // If it's been more than two minutes since the current location, use
    // the new location
    // because the user has likely moved
    if (isSignificantlyNewer) {
        return location;
        // If the new location is more than two minutes older, it must be
        // worse
    } else if (isSignificantlyOlder) {
        return currentBestLocation;
    }
    // Check whether the new location fix is more or less accurate
    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
            .getAccuracy());
    boolean isLessAccurate = accuracyDelta > 0;
    boolean isMoreAccurate = accuracyDelta < 0;
    boolean isSignificantlyLessAccurate = accuracyDelta > 200;
    // Check if the old and new location are from the same provider
    boolean isFromSameProvider = isSameProvider(location.getProvider(),
            currentBestLocation.getProvider());
    // Determine location quality using a combination of timeliness and
    // accuracy
    if (isMoreAccurate) {
        return location;
    } else if (isNewer && !isLessAccurate) {
        return location;
    } else if (isNewer && !isSignificantlyLessAccurate
            && isFromSameProvider) {
        return location;
    }
    return bestLocation;
}

/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
    if (provider1 == null) {
        return provider2 == null;
    }
    return provider1.equals(provider2);
}


看起来我们正在编写相同的应用程序;-)
这是我目前的实施。我仍处于GPS上传应用程序的beta测试阶段,因此可能会有许多改进。但到目前为止似乎工作得很好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
 * try to get the 'best' location selected from all providers
 */
private Location getBestLocation() {
    Location gpslocation = getLocationByProvider(LocationManager.GPS_PROVIDER);
    Location networkLocation =
            getLocationByProvider(LocationManager.NETWORK_PROVIDER);
    // if we have only one location available, the choice is easy
    if (gpslocation == null) {
        Log.d(TAG,"No GPS Location available.");
        return networkLocation;
    }
    if (networkLocation == null) {
        Log.d(TAG,"No Network Location available");
        return gpslocation;
    }
    // a locationupdate is considered 'old' if its older than the configured
    // update interval. this means, we didn't get a
    // update from this provider since the last check
    long old = System.currentTimeMillis() - getGPSCheckMilliSecsFromPrefs();
    boolean gpsIsOld = (gpslocation.getTime() < old);
    boolean networkIsOld = (networkLocation.getTime() < old);
    // gps is current and available, gps is better than network
    if (!gpsIsOld) {
        Log.d(TAG,"Returning current GPS Location");
        return gpslocation;
    }
    // gps is old, we can't trust it. use network location
    if (!networkIsOld) {
        Log.d(TAG,"GPS is old, Network is current, returning network");
        return networkLocation;
    }
    // both are old return the newer of those two
    if (gpslocation.getTime() > networkLocation.getTime()) {
        Log.d(TAG,"Both are old, returning gps(newer)");
        return gpslocation;
    } else {
        Log.d(TAG,"Both are old, returning network(newer)");
        return networkLocation;
    }
}

/**
 * get the last known location from a specific provider (network/gps)
 */
private Location getLocationByProvider(String provider) {
    Location location = null;
    if (!isProviderSupported(provider)) {
        return null;
    }
    LocationManager locationManager = (LocationManager) getApplicationContext()
            .getSystemService(Context.LOCATION_SERVICE);
    try {
        if (locationManager.isProviderEnabled(provider)) {
            location = locationManager.getLastKnownLocation(provider);
        }
    } catch (IllegalArgumentException e) {
        Log.d(TAG,"Cannot acces Provider" + provider);
    }
    return location;
}

编辑:这是从位置提供者请求定期更新的部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public void startRecording() {
    gpsTimer.cancel();
    gpsTimer = new Timer();
    long checkInterval = getGPSCheckMilliSecsFromPrefs();
    long minDistance = getMinDistanceFromPrefs();
    // receive updates
    LocationManager locationManager = (LocationManager) getApplicationContext()
            .getSystemService(Context.LOCATION_SERVICE);
    for (String s : locationManager.getAllProviders()) {
        locationManager.requestLocationUpdates(s, checkInterval,
                minDistance, new LocationListener() {

                    @Override
                    public void onStatusChanged(String provider,
                            int status, Bundle extras) {}

                    @Override
                    public void onProviderEnabled(String provider) {}

                    @Override
                    public void onProviderDisabled(String provider) {}

                    @Override
                    public void onLocationChanged(Location location) {
                        // if this is a gps location, we can use it
                        if (location.getProvider().equals(
                                LocationManager.GPS_PROVIDER)) {
                            doLocationUpdate(location, true);
                        }
                    }
                });
        // //Toast.makeText(this,"GPS Service STARTED",
        // Toast.LENGTH_LONG).show();
        gps_recorder_running = true;
    }
    // start the gps receiver thread
    gpsTimer.scheduleAtFixedRate(new TimerTask() {

        @Override
        public void run() {
            Location location = getBestLocation();
            doLocationUpdate(location, false);
        }
    }, 0, checkInterval);
}

public void doLocationUpdate(Location l, boolean force) {
    long minDistance = getMinDistanceFromPrefs();
    Log.d(TAG,"update received:" + l);
    if (l == null) {
        Log.d(TAG,"Empty location");
        if (force)
            Toast.makeText(this,"Current location not available",
                    Toast.LENGTH_SHORT).show();
        return;
    }
    if (lastLocation != null) {
        float distance = l.distanceTo(lastLocation);
        Log.d(TAG,"Distance to last:" + distance);
        if (l.distanceTo(lastLocation) < minDistance && !force) {
            Log.d(TAG,"Position didn't change");
            return;
        }
        if (l.getAccuracy() >= lastLocation.getAccuracy()
                && l.distanceTo(lastLocation) < l.getAccuracy() && !force) {
            Log.d(TAG,
                   "Accuracy got worse and we are still"
                      +"within the accuracy range.. Not updating");
            return;
        }
        if (l.getTime() <= lastprovidertimestamp && !force) {
            Log.d(TAG,"Timestamp not never than last");
            return;
        }
    }
    // upload/store your location here
}

需要考虑的事项:

  • 不要经常请求GPS更新,它会耗尽电池电量。我现在
    我的应用程序默认使用30分钟。

  • 添加"到最后已知位置的最小距离"检查。没有这个,你的观点
    当GPS不可用且位置正在被三角化时,它将"跳转"
    来自手机信号塔。或者您可以检查新位置是否超出准确性
    来自上一个已知位置的值。


要为您的应用选择正确的位置提供程序,您可以使用Criteria对象:

1
2
3
4
5
6
7
8
9
Criteria myCriteria = new Criteria();
myCriteria.setAccuracy(Criteria.ACCURACY_HIGH);
myCriteria.setPowerRequirement(Criteria.POWER_LOW);
// let Android select the right location provider for you
String myProvider = locationManager.getBestProvider(myCriteria, true);

// finally require updates at -at least- the desired rate
long minTimeMillis = 600000; // 600,000 milliseconds make 10 minutes
locationManager.requestLocationUpdates(myProvider,minTimeMillis,0,locationListener);

阅读requestLocationUpdates的文档,了解有关如何考虑参数的更多详细信息:

The frequency of notification may be controlled using the minTime and
minDistance parameters. If minTime is greater than 0, the LocationManager
could potentially rest for minTime milliseconds between location updates
to conserve power. If minDistance is greater than 0, a location will only
be broadcasted if the device moves by minDistance meters. To obtain
notifications as frequently as possible, set both parameters to 0.

更多的想法

  • 您可以使用Location.getAccuracy()监视Location对象的准确性,该值返回以米为单位的估计位置精度。
  • Criteria.ACCURACY_HIGH标准应该给你100米以下的误差,这不如GPS可以,但符合你的需要。
  • 您还需要监视位置提供程序的状态,并在用户不可用或禁用时切换到另一个提供程序。
  • 被动提供者也可以很好地匹配这种应用程序:想法是在其他应用程序请求并在系统范围内广播时使用位置更新。


回答前两点:

  • 如果启用并且周围没有厚墙,GPS将始终为您提供更精确的位置。

  • 如果location没有改变,那么你可以调用getLastKnownLocation(String)并立即检索位置。

使用替代方法:

您可以尝试获取正在使用的单元ID或所有相邻单元格

1
2
3
4
5
6
7
8
9
10
TelephonyManager mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
GsmCellLocation loc = (GsmCellLocation) mTelephonyManager.getCellLocation();
Log.d ("CID", Integer.toString(loc.getCid()));
Log.d ("LAC", Integer.toString(loc.getLac()));
// or
List<NeighboringCellInfo> list = mTelephonyManager.getNeighboringCellInfo ();
for (NeighboringCellInfo cell : list) {
    Log.d ("CID", Integer.toString(cell.getCid()));
    Log.d ("LAC", Integer.toString(cell.getLac()));
}

您可以通过几个开放数据库(例如,http://www.location-api.com/或http://opencellid.org/)引用单元格位置

策略是在读取位置时读取塔ID列表。然后,在下一个查询中(在您的应用中10分钟),再次阅读它们。如果至少有一些塔是相同的,那么使用getLastKnownLocation(String)是安全的。如果他们不是,那么等待onLocationChanged()。这避免了对该位置的第三方数据库的需要。您也可以尝试这种方法。


这是我的解决方案,运作得相当好:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
private Location bestLocation = null;
private Looper looper;
private boolean networkEnabled = false, gpsEnabled = false;

private synchronized void setLooper(Looper looper) {
    this.looper = looper;
}

private synchronized void stopLooper() {
    if (looper == null) return;
    looper.quit();
}

@Override
protected void runTask() {
    final LocationManager locationManager = (LocationManager) service
            .getSystemService(Context.LOCATION_SERVICE);
    final SharedPreferences prefs = getPreferences();
    final int maxPollingTime = Integer.parseInt(prefs.getString(
            POLLING_KEY,"0"));
    final int desiredAccuracy = Integer.parseInt(prefs.getString(
            DESIRED_KEY,"0"));
    final int acceptedAccuracy = Integer.parseInt(prefs.getString(
            ACCEPTED_KEY,"0"));
    final int maxAge = Integer.parseInt(prefs.getString(AGE_KEY,"0"));
    final String whichProvider = prefs.getString(PROVIDER_KEY,"any");
    final boolean canUseGps = whichProvider.equals("gps")
            || whichProvider.equals("any");
    final boolean canUseNetwork = whichProvider.equals("network")
            || whichProvider.equals("any");
    if (canUseNetwork)
        networkEnabled = locationManager
                .isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    if (canUseGps)
        gpsEnabled = locationManager
                .isProviderEnabled(LocationManager.GPS_PROVIDER);
    // If any provider is enabled now and we displayed a notification clear it.
    if (gpsEnabled || networkEnabled) removeErrorNotification();
    if (gpsEnabled)
        updateBestLocation(locationManager
                .getLastKnownLocation(LocationManager.GPS_PROVIDER));
    if (networkEnabled)
        updateBestLocation(locationManager
                .getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
    if (desiredAccuracy == 0
            || getLocationQuality(desiredAccuracy, acceptedAccuracy,
                    maxAge, bestLocation) != LocationQuality.GOOD) {
        // Define a listener that responds to location updates
        LocationListener locationListener = new LocationListener() {

            public void onLocationChanged(Location location) {
                updateBestLocation(location);
                if (desiredAccuracy != 0
                        && getLocationQuality(desiredAccuracy,
                                acceptedAccuracy, maxAge, bestLocation)
                                == LocationQuality.GOOD)
                    stopLooper();
            }

            public void onProviderEnabled(String provider) {
                if (isSameProvider(provider,
                        LocationManager.NETWORK_PROVIDER))networkEnabled =true;
                else if (isSameProvider(provider,
                        LocationManager.GPS_PROVIDER)) gpsEnabled = true;
                // The user has enabled a location, remove any error
                // notification
                if (canUseGps && gpsEnabled || canUseNetwork
                        && networkEnabled) removeErrorNotification();
            }

            public void onProviderDisabled(String provider) {
                if (isSameProvider(provider,
                        LocationManager.NETWORK_PROVIDER))networkEnabled=false;
                else if (isSameProvider(provider,
                        LocationManager.GPS_PROVIDER)) gpsEnabled = false;
                if (!gpsEnabled && !networkEnabled) {
                    showErrorNotification();
                    stopLooper();
                }
            }

            public void onStatusChanged(String provider, int status,
                    Bundle extras) {
                Log.i(LOG_TAG,"Provider" + provider +" statusChanged");
                if (isSameProvider(provider,
                        LocationManager.NETWORK_PROVIDER)) networkEnabled =
                        status == LocationProvider.AVAILABLE
                        || status == LocationProvider.TEMPORARILY_UNAVAILABLE;
                else if (isSameProvider(provider,
                        LocationManager.GPS_PROVIDER))
                    gpsEnabled = status == LocationProvider.AVAILABLE
                      || status == LocationProvider.TEMPORARILY_UNAVAILABLE;
                // None of them are available, stop listening
                if (!networkEnabled && !gpsEnabled) {
                    showErrorNotification();
                    stopLooper();
                }
                // The user has enabled a location, remove any error
                // notification
                else if (canUseGps && gpsEnabled || canUseNetwork
                        && networkEnabled) removeErrorNotification();
            }
        };
        if (networkEnabled || gpsEnabled) {
            Looper.prepare();
            setLooper(Looper.myLooper());
            // Register the listener with the Location Manager to receive
            // location updates
            if (canUseGps)
                locationManager.requestLocationUpdates(
                        LocationManager.GPS_PROVIDER, 1000, 1,
                        locationListener, Looper.myLooper());
            if (canUseNetwork)
                locationManager.requestLocationUpdates(
                        LocationManager.NETWORK_PROVIDER, 1000, 1,
                        locationListener, Looper.myLooper());
            Timer t = new Timer();
            t.schedule(new TimerTask() {

                @Override
                public void run() {
                    stopLooper();
                }
            }, maxPollingTime * 1000);
            Looper.loop();
            t.cancel();
            setLooper(null);
            locationManager.removeUpdates(locationListener);
        } else // No provider is enabled, show a notification
        showErrorNotification();
    }
    if (getLocationQuality(desiredAccuracy, acceptedAccuracy, maxAge,
            bestLocation) != LocationQuality.BAD) {
        sendUpdate(new Event(EVENT_TYPE, locationToString(desiredAccuracy,
                acceptedAccuracy, maxAge, bestLocation)));
    } else Log.w(LOG_TAG,"LocationCollector failed to get a location");
}

private synchronized void showErrorNotification() {
    if (notifId != 0) return;
    ServiceHandler handler = service.getHandler();
    NotificationInfo ni = NotificationInfo.createSingleNotification(
            R.string.locationcollector_notif_ticker,
            R.string.locationcollector_notif_title,
            R.string.locationcollector_notif_text,
            android.R.drawable.stat_notify_error);
    Intent intent = new Intent(
            android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
    ni.pendingIntent = PendingIntent.getActivity(service, 0, intent,
            PendingIntent.FLAG_UPDATE_CURRENT);
    Message msg = handler.obtainMessage(ServiceHandler.SHOW_NOTIFICATION);
    msg.obj = ni;
    handler.sendMessage(msg);
    notifId = ni.id;
}

private void removeErrorNotification() {
    if (notifId == 0) return;
    ServiceHandler handler = service.getHandler();
    if (handler != null) {
        Message msg = handler.obtainMessage(
                ServiceHandler.CLEAR_NOTIFICATION, notifId, 0);
        handler.sendMessage(msg);
        notifId = 0;
    }
}

@Override
public void interrupt() {
    stopLooper();
    super.interrupt();
}

private String locationToString(int desiredAccuracy, int acceptedAccuracy,
        int maxAge, Location location) {
    StringBuilder sb = new StringBuilder();
    sb.append(String.format(
           "qual=%s time=%d prov=%s acc=%.1f lat=%f long=%f",
            getLocationQuality(desiredAccuracy, acceptedAccuracy, maxAge,
                    location), location.getTime() / 1000, // Millis to
                                                            // seconds
            location.getProvider(), location.getAccuracy(), location
                    .getLatitude(), location.getLongitude()));
    if (location.hasAltitude())
        sb.append(String.format(" alt=%.1f", location.getAltitude()));
    if (location.hasBearing())
        sb.append(String.format(" bearing=%.2f", location.getBearing()));
    return sb.toString();
}

private enum LocationQuality {
    BAD, ACCEPTED, GOOD;

    public String toString() {
        if (this == GOOD) return"Good";
        else if (this == ACCEPTED) return"Accepted";
        else return"Bad";
    }
}

private LocationQuality getLocationQuality(int desiredAccuracy,
        int acceptedAccuracy, int maxAge, Location location) {
    if (location == null) return LocationQuality.BAD;
    if (!location.hasAccuracy()) return LocationQuality.BAD;
    long currentTime = System.currentTimeMillis();
    if (currentTime - location.getTime() < maxAge * 1000
            && location.getAccuracy() <= desiredAccuracy)
        return LocationQuality.GOOD;
    if (acceptedAccuracy == -1
            || location.getAccuracy() <= acceptedAccuracy)
        return LocationQuality.ACCEPTED;
    return LocationQuality.BAD;
}

private synchronized void updateBestLocation(Location location) {
    bestLocation = getBestLocation(location, bestLocation);
}

protected Location getBestLocation(Location location,
        Location currentBestLocation) {
    if (currentBestLocation == null) {
        // A new location is always better than no location
        return location;
    }
    if (location == null) return currentBestLocation;
    // Check whether the new location fix is newer or older
    long timeDelta = location.getTime() - currentBestLocation.getTime();
    boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
    boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
    boolean isNewer = timeDelta > 0;
    // If it's been more than two minutes since the current location, use
    // the new location
    // because the user has likely moved
    if (isSignificantlyNewer) {
        return location;
        // If the new location is more than two minutes older, it must be
        // worse
    } else if (isSignificantlyOlder) {
        return currentBestLocation;
    }
    // Check whether the new location fix is more or less accurate
    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
            .getAccuracy());
    boolean isLessAccurate = accuracyDelta > 0;
    boolean isMoreAccurate = accuracyDelta < 0;
    boolean isSignificantlyLessAccurate = accuracyDelta > 200;
    // Check if the old and new location are from the same provider
    boolean isFromSameProvider = isSameProvider(location.getProvider(),
            currentBestLocation.getProvider());
    // Determine location quality using a combination of timeliness and
    // accuracy
    if (isMoreAccurate) {
        return location;
    } else if (isNewer && !isLessAccurate) {
        return location;
    } else if (isNewer && !isSignificantlyLessAccurate
            && isFromSameProvider) {
        return location;
    }
    return bestLocation;
}

/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
    if (provider1 == null) return provider2 == null;
    return provider1.equals(provider2);
}


位置准确性主要取决于所使用的位置提供商:

  • GPS - 将为您提供几米精度(假设您有GPS接收)
  • 无线网络 - 将为您提供几百米的精度
  • Cell Network - 会给你带来非常不准确的结果(我已经看到了4公里的偏差...)
  • 如果您正在寻找它的准确性,那么GPS是您唯一的选择。

    我在这里阅读了一篇非常翔实的文章。

    至于GPS超时 - 60秒就足够了,在大多数情况下甚至太多了。我认为30秒是可以的,有时甚至不到5秒......

    如果您只需要一个位置,我建议您在onLocationChanged方法中,一旦收到更新,您将取消注册监听器并避免不必要地使用GPS。


    目前我正在使用,因为这对我的应用程序获取位置和计算距离是可靠的......我正在使用这个用于我的出租车应用程序。

    使用谷歌开发人员开发的融合API,GPS传感器,磁力计,加速度计的融合也使用Wifi或小区位置来计算或估计位置。它还能够准确地在建筑物内部提供位置更新。
    细节得到链接
    https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    import android.app.Activity;
    import android.location.Location;
    import android.os.Bundle;
    import android.support.v7.app.ActionBarActivity;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.widget.TextView;
    import android.widget.Toast;

    import com.google.android.gms.common.ConnectionResult;
    import com.google.android.gms.common.GooglePlayServicesUtil;
    import com.google.android.gms.common.api.GoogleApiClient;
    import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
    import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
    import com.google.android.gms.location.LocationListener;
    import com.google.android.gms.location.LocationRequest;
    import com.google.android.gms.location.LocationServices;

    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;


    public class MainActivity extends Activity implements LocationListener,
            GoogleApiClient.ConnectionCallbacks,
            GoogleApiClient.OnConnectionFailedListener {

        private static final long ONE_MIN = 500;
        private static final long TWO_MIN = 500;
        private static final long FIVE_MIN = 500;
        private static final long POLLING_FREQ = 1000 * 20;
        private static final long FASTEST_UPDATE_FREQ = 1000 * 5;
        private static final float MIN_ACCURACY = 1.0f;
        private static final float MIN_LAST_READ_ACCURACY = 1;

        private LocationRequest mLocationRequest;
        private Location mBestReading;
    TextView tv;
        private GoogleApiClient mGoogleApiClient;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            if (!servicesAvailable()) {
                finish();
            }

            setContentView(R.layout.activity_main);
    tv= (TextView) findViewById(R.id.tv1);
            mLocationRequest = LocationRequest.create();
            mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
            mLocationRequest.setInterval(POLLING_FREQ);
            mLocationRequest.setFastestInterval(FASTEST_UPDATE_FREQ);

            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addApi(LocationServices.API)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .build();


            if (mGoogleApiClient != null) {
                mGoogleApiClient.connect();
            }
        }

        @Override
        protected void onResume() {
            super.onResume();

            if (mGoogleApiClient != null) {
                mGoogleApiClient.connect();
            }
        }

        @Override
        protected void onPause() {d
            super.onPause();

            if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
                mGoogleApiClient.disconnect();
            }
        }


            tv.setText(location +"");
            // Determine whether new location is better than current best
            // estimate
            if (null == mBestReading || location.getAccuracy() < mBestReading.getAccuracy()) {
                mBestReading = location;


                if (mBestReading.getAccuracy() < MIN_ACCURACY) {
                    LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
                }
            }
        }

        @Override
        public void onConnected(Bundle dataBundle) {
            // Get first reading. Get additional location updates if necessary
            if (servicesAvailable()) {

                // Get best last location measurement meeting criteria
                mBestReading = bestLastKnownLocation(MIN_LAST_READ_ACCURACY, FIVE_MIN);

                if (null == mBestReading
                        || mBestReading.getAccuracy() > MIN_LAST_READ_ACCURACY
                        || mBestReading.getTime() < System.currentTimeMillis() - TWO_MIN) {

                    LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);

                   //Schedule a runnable to unregister location listeners

                        @Override
                        public void run() {
                            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, MainActivity.this);

                        }

                    }, ONE_MIN, TimeUnit.MILLISECONDS);

                }

            }
        }

        @Override
        public void onConnectionSuspended(int i) {

        }


        private Location bestLastKnownLocation(float minAccuracy, long minTime) {
            Location bestResult = null;
            float bestAccuracy = Float.MAX_VALUE;
            long bestTime = Long.MIN_VALUE;

            // Get the best most recent location currently available
            Location mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
            //tv.setText(mCurrentLocation+"");
            if (mCurrentLocation != null) {
                float accuracy = mCurrentLocation.getAccuracy();
                long time = mCurrentLocation.getTime();

                if (accuracy < bestAccuracy) {
                    bestResult = mCurrentLocation;
                    bestAccuracy = accuracy;
                    bestTime = time;
                }
            }

            // Return best reading or null
            if (bestAccuracy > minAccuracy || bestTime < minTime) {
                return null;
            }
            else {
                return bestResult;
            }
        }

        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {

        }

        private boolean servicesAvailable() {
            int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

            if (ConnectionResult.SUCCESS == resultCode) {
                return true;
            }
            else {
                GooglePlayServicesUtil.getErrorDialog(resultCode, this, 0).show();
                return false;
            }
        }
    }


    Android-ReactiveLocation库是另一个处理Android位置的好库。

    Small library that wraps Google Play Service API in brilliant RxJava
    Observables reducing boilerplate to minimum.

    https://github.com/mcharmas/Android-ReactiveLocation


    我使用谷歌建议的最新位置拉动方法(使用FusedLocationProviderClient)在互联网上搜索更新(过去一年)的答案。我终于登陆了这个:

    https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates

    我创建了一个新项目并复制了大部分代码。繁荣。有用。我认为没有任何弃用的行。

    此外,模拟器似乎没有获得GPS位置,我知道。它确实在日志中报告了这一点:"满足所有位置设置。"

    最后,如果你想知道(我做过),你不需要谷歌开发者控制台的谷歌地图api密钥,如果你想要的只是GPS位置。

    他们的教程也很有用。但我想要一个完整的单页教程/代码示例。他们的教程堆栈,但是当你刚接触到它时会感到困惑,因为你不知道从早期页面中需要哪些部分。

    https://developer.android.com/training/location/index.html

    最后,记住这样的事情:

    我不仅要修改mainActivity.Java。我还必须修改Strings.xml,androidmanifest.xml和正确的build.gradle。还有你的activity_Main.xml(但那部分对我来说很容易)。

    我需要添加像这样的依赖项:implementation'com.google.android.gms:play-services-location:11.8.0',并更新我的android studio SDK的设置以包含google play服务。 (文件设置外观系统设置android SDK SDK工具检查google play服务)。

    更新:android模拟器似乎确实获得了位置和位置更改事件(当我更改了sim设置中的值时)。但我最好的和最初的结果是在一个实际的设备上。因此,在实际设备上进行测试可能最容易。


    嗨,这是一个链接,它将能够提供整个源代码以整合gps位置的链接,该位置将能够通过gps跟踪任何人,ti将通知:

    喜欢:http://code.google.com/p/mytracks/


    最近重构了获取代码的位置,学习了一些好的想法,最后实现了一个比较完善的库和Demo。

    @Gryphius的回答很好

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
        //request all valid provider(network/gps)
    private boolean requestAllProviderUpdates() {
        checkRuntimeEnvironment();
        checkPermission();

        if (isRequesting) {
            EasyLog.d("Request location update is busy");
            return false;
        }


        long minTime = getCheckTimeInterval();
        float minDistance = getCheckMinDistance();

        if (mMapLocationListeners == null) {
            mMapLocationListeners = new HashMap<>();
        }

        mValidProviders = getValidProviders();
        if (mValidProviders == null || mValidProviders.isEmpty()) {
            throw new IllegalArgumentException("Not available provider.");
        }

        for (String provider : mValidProviders) {
            LocationListener locationListener = new LocationListener() {
                @Override
                public void onLocationChanged(Location location) {
                    if (location == null) {
                        EasyLog.e("LocationListener callback location is null.");
                        return;
                    }
                    printf(location);
                    mLastProviderTimestamp = location.getTime();

                    if (location.getProvider().equals(LocationManager.GPS_PROVIDER)) {
                        finishResult(location);
                    } else {
                        doLocationResult(location);
                    }

                    removeProvider(location.getProvider());
                    if (isEmptyValidProviders()) {
                        requestTimeoutMsgInit();
                        removeUpdates();
                    }
                }

                @Override
                public void onStatusChanged(String provider, int status, Bundle extras) {
                }

                @Override
                public void onProviderEnabled(String provider) {
                }

                @Override
                public void onProviderDisabled(String provider) {
                }
            };
            getLocationManager().requestLocationUpdates(provider, minTime, minDistance, locationListener);
            mMapLocationListeners.put(provider, locationListener);
            EasyLog.d("Location request %s provider update.", provider);
        }
        isRequesting = true;
        return true;
    }

    //remove request update
    public void removeUpdates() {
        checkRuntimeEnvironment();

        LocationManager locationManager = getLocationManager();
        if (mMapLocationListeners != null) {
            Set<String> keys = mMapLocationListeners.keySet();
            for (String key : keys) {
                LocationListener locationListener = mMapLocationListeners.get(key);
                if (locationListener != null) {
                    locationManager.removeUpdates(locationListener);
                    EasyLog.d("Remove location update, provider is" + key);
                }
            }
            mMapLocationListeners.clear();
            isRequesting = false;
        }
    }

    //Compared with the last successful position, to determine whether you need to filter
    private boolean isNeedFilter(Location location) {
        checkLocation(location);

        if (mLastLocation != null) {
            float distance = location.distanceTo(mLastLocation);
            if (distance < getCheckMinDistance()) {
                return true;
            }
            if (location.getAccuracy() >= mLastLocation.getAccuracy()
                    && distance < location.getAccuracy()) {
                return true;
            }
            if (location.getTime() <= mLastProviderTimestamp) {
                return true;
            }
        }
        return false;
    }

    private void doLocationResult(Location location) {
        checkLocation(location);

        if (isNeedFilter(location)) {
            EasyLog.d("location need to filtered out, timestamp is" + location.getTime());
            finishResult(mLastLocation);
        } else {
            finishResult(location);
        }
    }

    //Return to the finished position
    private void finishResult(Location location) {
        checkLocation(location);

        double latitude = location.getLatitude();
        double longitude = location.getLongitude();
        float accuracy = location.getAccuracy();
        long time = location.getTime();
        String provider = location.getProvider();

        if (mLocationResultListeners != null && !mLocationResultListeners.isEmpty()) {
            String format ="Location result:<%f, %f> Accuracy:%f Time:%d Provider:%s";
            EasyLog.i(String.format(format, latitude, longitude, accuracy, time, provider));

            mLastLocation = location;
            synchronized (this) {
                Iterator<LocationResultListener> iterator =  mLocationResultListeners.iterator();
                while (iterator.hasNext()) {
                    LocationResultListener listener = iterator.next();
                    if (listener != null) {
                        listener.onResult(location);
                    }
                    iterator.remove();
                }
            }
        }
    }

    完成实施:
    https://github.com/bingerz/FastLocation/blob/master/fastlocationlib/src/main/java/cn/bingerz/fastlocation/FastLocation.java

    1.感谢@Gryphius解决方案的想法,我也分享了完整的代码。

    2.每次请求完成位置,最好删除更新,否则手机状态栏将始终显示定位图标


    根据我的经验,我发现最好使用GPS定位,除非它不可用。我不太了解其他位置提供商,但我知道对于GPS,有一些技巧可以用来给出一些贫民窟精确度量。海拔通常是一个标志,所以你可以检查荒谬的价值观。 Android位置修复程序有准确度衡量标准。此外,如果您可以看到使用的卫星数量,这也可以指示精度。

    一个有趣的方法来更好地了解准确性可能是非常迅速地要求一组修复,例如~1 /秒持续10秒然后再睡一两分钟。我去过的一次谈话导致相信一些Android设备无论如何都会这样做。然后你会淘汰异常值(我听过这里提到的卡尔曼滤波器)并使用某种中心策略来获得单一修复。

    显然,你到达这里的深度取决于你的要求有多难。如果您对获得最佳位置有特别严格的要求,我想您会发现GPS和网络位置与苹果和橙子相似。 GPS也可能因设备而异。


    Skyhook(http://www.skyhookwireless.com/)的位置提供商比Google提供的标准提供商快得多。它可能正是你要找的东西。我不隶属于他们。