在Android中获取用户位置的好方法

存在的问题:

尽快在阈值范围内获取用户的当前位置,同时节省电池。

为什么这个问题是问题:

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

这里的“更好”是指速度与准确度的比值 如果我能在不打开GPS的情况下立即得到位置,我愿意牺牲几米的精度

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

谷歌有一个确定“最佳”位置的例子:http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
但是我认为它远不如它应该/可能的那么好

我有点困惑为什么谷歌没有一个规范化的API的位置,开发人员不应该关心位置从哪里来,你应该指定你想要什么,手机应该为你选择。

我需要帮助的方面:

我需要找到一种确定“最佳”位置的好方法,可能是通过一些启发式或第三方库。

< p > 这并不意味着要确定最好的提供者! < br > 我可能会使用所有的提供者,并选择其中最好的

app背景:

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

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

Misc:

你认为期望和接受的精度的合理值是多少?< br > 我一直在用接受的100万和想要的30万,这要求太高了吗?< br > 我希望以后能够在地图上绘制用户的路径 要求100米,接受500米更好吗?< / p >

另外,现在我的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);
}
152250 次浏览

回答前两点:

  • 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分钟),再次阅读它们。如果至少有一些塔是相同的,那么使用getLastKnownLocation(String)是安全的。如果不是,则等待onLocationChanged()。这避免了对位置的第三方数据库的需要。你也可以尝试这种方法

定位精度主要取决于所使用的位置提供程序:

  1. GPS -将为您提供几米的精度(假设您有GPS接收)
  2. Wifi -将给你几百米的精度
  3. 蜂窝网络-会给你非常不准确的结果(我见过高达4公里的偏差…)

如果你想要的是精度,那么GPS是你唯一的选择。

我读过一篇关于它的非常翔实的文章。

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

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

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

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 . properties将自动返回 可能会在位置更新之间休息minTime毫秒 为了节约能源。如果minDistance大于0,一个位置只会 如果设备按minDistance米移动,则广播。获得 尽可能频繁地通知,将两个参数都设置为0

更多的想法

  • 你可以使用Location.getAccuracy ()来监视Location对象的精度,它返回以米为单位的位置的估计精度。
  • Criteria.ACCURACY_HIGH标准应该给你低于100米的误差,这不如GPS,但符合你的需求。
  • 您还需要监视位置提供程序的状态,并在它不可用或被用户禁用时切换到另一个提供程序。
  • 被动的提供者也可能是这类应用程序的一个很好的匹配:其思想是在另一个应用程序请求并在系统范围内广播时使用位置更新。
看起来我们在编写相同的应用程序;-)
这是我当前的实现。我的GPS上传应用程序仍处于beta测试阶段,因此可能会有许多可能的改进。但到目前为止,它似乎工作得很好
/**
* 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分钟作为默认的应用程序。李< / p > < / >

  • 添加一个“到最后已知位置的最小距离”检查。没有这个,你的分数 当GPS无法使用,位置被三角定位时,会“跳来跳去”吗 从发射塔传来的。或者您可以检查新位置是否在精度范围之外 来自最后一个已知位置的值。

Skyhook (http://www.skyhookwireless.com/)提供的位置提供程序比谷歌提供的标准程序快得多。也许这就是你要找的。我和他们没有关系。

根据我的经验,我发现最好使用GPS修复,除非它不可用。我不太了解其他位置提供商,但我知道对于GPS来说,有一些技巧可以用来提供一个小范围的精确测量。海拔高度通常是一个符号,所以你可以检查一些荒谬的值。Android定位修复有准确性测量。此外,如果你能看到使用的卫星数量,这也可以表明精度。

要更好地了解精确度,一个有趣的方法是快速地请求一组修复,比如10秒内每秒1次,然后休眠一两分钟。我参加过的一个演讲让我相信一些android设备无论如何都会做到这一点。然后,您将剔除异常值(我在这里听到过卡尔曼滤波器),并使用某种集中策略来获得单个修复。

显然,你能达到的深度取决于你的要求有多难。如果你对尽可能获得最佳位置有特别严格的要求,我想你会发现GPS和网络定位就像苹果和橘子一样相似。另外,GPS在不同的设备上也有很大的不同。

这是我的解决方案,效果相当不错:

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);
}

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

使用谷歌开发人员开发的融合API,融合GPS传感器,磁力计,加速度计,还使用Wifi或单元定位来计算或估计位置。它还能够准确地提供建筑物内部的位置更新。 详情请访问链接 https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi < / p >

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;
}
}
}

我在互联网上搜索了一个更新的(去年)答案,使用谷歌建议的最新位置提取方法(使用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(但这部分对我来说很简单)。

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

更新:android模拟器似乎得到了一个位置和位置变化事件(当我改变sim设置中的值时)。但我最好的和第一个结果是在实际设备上。所以在实际设备上进行测试可能是最简单的。

最近重构得到的位置代码,学习了一些不错的思路,最后实现了一个比较完善的库和Demo。

@Gryphius的回答很好

    //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();
}
}
}
}
< p >完整的实现: https://github.com/bingerz/FastLocation/blob/master/fastlocationlib/src/main/java/cn/bingerz/fastlocation/FastLocation.java < / p >

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

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