在Android中获取用户位置的好方法
问题:
尽快获取用户的当前位置,同时节省电量。
为什么问题是一个问题:
首先,android有两个提供者; networking和GPS。 有时networking更好,有时候GPS更好。
通过“更好”我的意思是速度与准确率。
我愿意牺牲几米的准确度,如果我几乎可以立即得到位置而不打开GPS。
其次,如果您请求更新位置更改,则当前位置稳定时不会发送任何内容。
Google有一个确定“最佳”位置的例子: http : //developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
但是我认为这不是接近应该/可能的地方。
我有点困惑,为什么谷歌没有一个规范化的API位置,开发人员不应该关心的位置来自,你应该只是指定你想要的和手机应该为你select。
我需要帮助的是:
我需要find一个确定“最佳”位置的好方法,也许可以通过一些启发式的方法,或者通过一些第三方库。
这并不意味着确定最好的提供者!
我可能会使用所有提供商,并select最好的。
应用程序的背景:
应用程序将以固定的时间间隔收集用户的位置(比方说每隔10分钟左右)并将其发送到服务器。
该应用程序应尽可能节约电量,位置应具有X(50-100?)米精度。
目标是稍后能够在地图上绘制一天中的用户path,所以我需要足够的准确性。
其他:
你认为什么是理想值和接受精度的合理值?
我已经按照要求使用了100米和30米,这个要问多less?
我希望能够稍后在地图上绘制用户的path。
需要的是100米,接受的是500米?
另外,现在我的GPS每个位置更新的最大时间为60秒,如果你在室内的话,这个距离太短,可能是200米的精度?
这是我目前的代码,任何反馈意见(除了没有错误检查是TODO):
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上传器应用程序的testing阶段,所以可能有很多可能的改进。 但迄今为止似乎工作得很好。
/** * 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; }
编辑:这是从位置提供程序请求定期更新的部分:
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不可用时,你的点将会“跳来跳去”,并且这个位置正在从信元塔中进行三angular测量。 或者您可以检查新位置是否在最后一个已知位置的准确度值之外。
要为您的应用程序select正确的位置提供程序,可以使用Criteria对象:
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的文档以获取有关如何考虑参数的更多详细信息:
通知的频率可以使用minTime和minDistance参数进行控制。 如果minTime大于0,则LocationManager可能会在位置更新之间restminTime毫秒以节省电量。 如果minDistance大于0,则只有当设备通过minDistance米移动时才会广播一个位置。 要尽可能频繁地获取通知,请将这两个参数设置为0。
更多的想法
- 您可以使用Location.getAccuracy()来监视位置对象的精确度,它将以米为单位返回位置的估计精度。
-
Criteria.ACCURACY_HIGH
标准应该给你100m以下的错误,这是不如GPS可以,但符合你的需求。 - 您还需要监视您的位置提供商的状态,如果用户不可用或禁用,请切换到另一个提供商。
- 被动提供商也可能是这种应用的一个很好的匹配:这个想法是,当他们被另一个应用请求并且在全系统广播时使用位置更新。
回答前两点 :
-
GPS将始终为您提供更精确的位置, 如果启用,并且周围没有厚壁 。
-
如果位置没有改变,那么你可以调用getLastKnownLocation(String)并立即检索位置。
使用另一种方法 :
您可以尝试获取正在使用的单元格ID或所有相邻的单元格
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分钟在您的应用程序),再读一遍。 如果至less有一些塔是相同的,那么使用getLastKnownLocation(String)
是安全的。 如果他们不是,那么等待onLocationChanged()
。 这避免了位置的第三方数据库的需要。 你也可以尝试这种方法 。
这是我的解决scheme,工作得很好:
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接收)
- 无线 – 将得到你几百米的准确性
- 细胞networking – 会给你非常不准确的结果(我见过长达4公里的偏差…)
如果您正在寻找精度,那么GPS是您唯一的select。
我在这里阅读了一篇非常丰富的文章。
至于GPS超时时间 – 60秒应该足够了,而且在大多数情况下还是太多了。 我认为30秒是好的,有时甚至不到5秒…
如果你只需要一个位置,我build议你的onLocationChanged
方法,一旦你收到一个更新,你将取消注册监听器,并避免不必要的使用GPS。
Android-ReactiveLocation库是处理Android位置的另一个不错的库。
将精巧的RxJava Observables封装成Google Play Service API的小型库,将样板文件降至最低。
嗨,这是一个链接,将能够给整个源代码整合的gps位置,将能够跟踪任何人的GPS和ti将通知:
如: http : //code.google.com/p/mytracks/
目前我正在使用,因为这是值得信赖的获取位置和计算距离为我的应用程序……我用这个为我的士应用程序。
使用Google开发者开发的Fusion API与GPS Sensor,Magnetometer,Accelerometer的融合也使用Wifi或Cell位置计算或估计位置。 它也能够在build筑物内精确地给出位置更新。 有关详情, 请访问https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi
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; } } }
根据我的经验,我发现最好去GPS定位,除非它不可用。 我对其他位置提供商知之甚less,但我知道,对于GPS来说,有一些技巧可以用来给出一些贫民区的精确度。 高度往往是一个标志,所以你可以检查荒谬的价值观。 Android定位上有准确的措施。 另外,如果您可以看到使用的卫星数量,这也可以指示精度。
获得更好的精度的一个有趣的方法可能是要求一组修复非常迅速,如约10秒/秒,然后睡一两分钟。 我一直在谈的一个话题就是相信一些Android设备会这样做。 然后,你会去除exception值(我已经听说卡尔曼滤波器在这里提到),并使用某种中心策略来获得一个单一的修复。
显然,你到这里的深度取决于你的要求有多难。 如果您有特别严格的要求来获得最佳位置,我想您会发现GPS和networking位置与苹果和橙子相似。 另外,GPS可能会随着设备的不同而大不相同。
Skyhook(http://www.skyhookwireless.com/)的位置提供商比Google提供的标准提供商要快得多。; 这可能是你在找什么。 我不隶属于他们。