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分钟),再次阅读它们。如果至少有一些塔是相同的,那么使用
这是我的解决方案,运作得相当好:
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超时 - 60秒就足够了,在大多数情况下甚至太多了。我认为30秒是可以的,有时甚至不到5秒......
如果您只需要一个位置,我建议您在
目前我正在使用,因为这对我的应用程序获取位置和计算距离是可靠的......我正在使用这个用于我的出租车应用程序。
使用谷歌开发人员开发的融合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提供的标准提供商快得多。它可能正是你要找的东西。我不隶属于他们。