当我试图在Android中获取设备位置时,我遇到了一个奇怪的问题。
有时它会很好地获取当前位置,但有时候它会一直返回旧位置。我觉得它已经老了几天,距它应该的位置几十甚至几百公里。
我不知道我做错了什么,我尝试了几种不同的方法来获取位置,但似乎每种方法都有同样的问题。
最奇怪的是它不会发生在每个设备中......或者至少它看起来像是这样。三星S3和S4受影响最大,但在我的nexus 4中它从未发生过。
这是我的代码,也许你看错了:
public void startSearchingLocation() {
// Define a listener that responds to location updates
locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
// Called when a new location is found by the network location
// provider.
makeUseOfNewLocation(location);
}
@Override
public void onStatusChanged(String provider, int status,
Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
};
String provider = getProvider();
/*I ignore the passive provider*/
if (provider == null
|| provider.contains(LocationManager.PASSIVE_PROVIDER)) {
this.validProvider = false;
} else {
this.validProvider = true;
}
if (validProvider) {
locationManager.requestLocationUpdates(provider, time, distance,
locationListener);
}
}
protected void makeUseOfNewLocation(Location location) {
JSONObject obj = new JSONObject();
try {
obj.put("latitude", location.getLatitude());
obj.put("longitude", location.getLongitude());
obj.put("provider", location.getProvider());
locationResult = obj;
} catch (Exception e) {
e.printStackTrace();
}
}
public JSONObject getLastKnownLocation() {
if (locationResult != null) {
return locationResult;
} else {
String provider = getProvider();
Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
JSONObject obj = new JSONObject();
try {
obj.put("latitude", location.getLatitude());
obj.put("longitude", location.getLongitude());
obj.put("provider", location.getProvider());
return obj;
} catch (Exception e) {
e.printStackTrace();
}
}
}
return null;
}
public String getProvider() {
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setPowerRequirement(Criteria.POWER_LOW);
criteria.setAltitudeRequired(false);
criteria.setBearingRequired(false);
criteria.setSpeedRequired(false);
String provider = locationManager.getBestProvider(criteria, true);
return provider;
}
由于
注意:互联网连接不错。一些用户开始抱怨这个问题。而使用地理定位的其他应用程序工作正常。它必须是我的代码中的东西,但我无法弄清楚是什么。 最奇怪的是它似乎在一两个星期内正常工作,然后它开始在很多用户同时发生
答案 0 :(得分:4)
我会说问题出在
中criteria.setPowerRequirement(Criteria.POWER_LOW);
可能只是返回被动位置提供者。我会删除它,用户现在知道这种应用消耗电池,因此它不会成为强制标准。
无论如何,我有一个更复杂的类来检索位置。让我解释一下。
首先,from here我使用这个很棒的功能来确定一个位置读数是否优于当前位置。
/**
* @param location
* The new Location that you want to evaluate
* @param currentBestLocation
* The current Location fix, to which you want to compare the new
* one
*/
protected boolean isBetterLocation(Location location,
Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
// 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 true;
// If the new location is more than two minutes older, it must be
// worse
} else if (isSignificantlyOlder) {
return false;
}
// 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 true;
} else if (isNewer && !isLessAccurate) {
return true;
} else if (isNewer && !isSignificantlyLessAccurate
&& isFromSameProvider) {
return true;
}
return false;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
然后,我创建自己的类来实现LocationListener(活动或帮助类,由您决定)。我只考虑gps和网络提供商,但它很容易扩展
public class LocationObserver implements LocationListener {
LocationManager lm;
boolean gps_enabled = false;
boolean network_enabled = false, network_connected = false;
Context context;
Long timeout;
int minTime;
int minDistance;
Handler handler = new Handler();
private Location currentLocation = null;
public LocationObserver(Context pContext, int minTime,
int minDistance, long timeout) {
this.context = pContext;
this.timeout = timeout;
this.minDistance = minDistance;
this.minTime = minTime;
}
我创建了一个方法开始,我认为哪个是最好的提供者,或者我可以听多个
public void start() {
if (lm == null)
lm = (LocationManager) context
.getSystemService(Context.LOCATION_SERVICE);
// exceptions will be thrown if provider is not permitted.
try {
gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch (Exception ex) {
//dont worry yet
}
try {
network_enabled = lm
.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
network_connected = activeNetwork != null
&& activeNetwork.isConnectedOrConnecting();
} catch (Exception ex) {
//dont wory yet
}
//now, lets see what happend,
//don't start listeners if no provider is enabled
if (!gps_enabled && !network_enabled) {
//nothing enabled, alert the user to enable any provide
//Settings.ACTION_LOCATION_SOURCE_SETTINGS;
}
else { // one provider is enabled, and the other one is not
if (gps_enabled) { // gps enabled, network disabled
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER,
minTime, minDistance, this);
//Consider asking the user to also enable network location ( Settings.ACTION_LOCATION_SOURCE_SETTINGS;=
} else { // gps disabled, network enabled
// check if actually there is a conection
if (network_connected) {
lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
minTime, minDistance, this);
//OK, Network is working,
//consider asking the user to enable also gps (Settings.ACTION_LOCATION_SOURCE_SETTINGS;)
}
//if network is not enabled, having the network location enabled is useles
else {
//ask the user to connect to any network ( Settings.ACTION_WIRELESS_SETTINGS)
}
}
}
//retrieve the lastKnownLocation (we will see this function later) and call the callback to start using it
handler.postDelayed(new Runnable() {
@Override
public void run() {
Location l = getLastKnownLocation();
onLocationChanged(l);
}
}, timeout);
}
我们制作了自己的getLastKnownLocation(),它考虑了所有已启用的提供程序,并返回了最佳位置
public Location getLastKnownLocation() {
Location net_loc = null, gps_loc = null;
if (gps_enabled)
gps_loc = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (network_enabled)
net_loc = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
// if there are both values use the best one
if (gps_loc != null && net_loc != null) {
if (isBetterLocation(gps_loc, net_loc))
return gps_loc;
else
return net_loc;
}
else if (gps_loc != null) {
return gps_loc;
} else if (net_loc != null) {
return net_loc;
} else
return null;
}
最后,我们编写onLocationChanged回调,提供者将使用任何更新调用。由于所有提供商都会调用它,可能即将到来的位置比我们已经拥有的位置更差,所以我们也比较它们
public synchronized void onLocationChanged(Location location) {
if (location == null || currentLocation == null
|| isBetterLocation(location, currentLocation)) {
currentLocation = location;
//do whatever you need with the new location
}
}
}
另外,我用来做的是如果一个位置在第一次调用之后进行getlastknowlocation,则继续检查计时器。如果特定时间过去了,我会检查所有内容,查看已启用的提供程序,或再次要求用户启用已禁用的提供程序。
此外,有些人喜欢直接依赖Play服务位置Apis,您不必担心提供商,也不必专注于底层定位技术的细节。 https://developer.android.com/google/play-services/location.html
答案 1 :(得分:1)
我认为你在不期望的时候使用getLastKnownLocation()
。 getLastKnownLocation()
的实施使用位置管理器的最后结果(如果可用),或者如果没有可用的话,则使用最后的已知位置。因此,如果您的用户关闭了位置服务,您的应用会从makeUseOfNewLocation()
继续使用它看到的最后一个位置,从locationManager.getLastKnownLocation(provider)
或手机看到的最后一个位置{{1}}。这可以解释您所看到的行为,即代码连续返回相同的旧位置。
要解决此问题,您可以使用回调onProviderEnabled / onProviderDisabled来通知您的应用它不应该继续使用lastKnownLocation。您还可以使用locationManager.isProviderEnabled()或locationClient.isConnected()在使用位置之前检查您的位置服务是否已连接,并提示用户重新启用它们。
答案 2 :(得分:1)
Location location = locationManager.getLastKnownLocation(provider);
造成了这个问题。 getLastKnownLocation(provider)
提供用户智能手机中任何基于位置的应用程序收集或使用的最后位置。
您可以使用 Sharedpreferences 在应用程序中保存位置数据,以便在需要时将其用作lastknownlocation。
答案 3 :(得分:0)
以这种方式尝试:
并使用bestProvider
// Getting LocationManager object from System Service LOCATION_SERVICE
LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
// Creating a criteria object to retrieve provider
Criteria criteria = new Criteria();
// Getting the name of the best provider
String provider = locationManager.getBestProvider(criteria, true);
// Getting Current Location From GPS
Location location = locationManager.getLastKnownLocation(provider);
if(location!=null)
{
onLocationChanged(location);
}
locationManager.requestLocationUpdates(provider, 20000, 0, this);
答案 4 :(得分:0)
AFAIK位置源自Android设备可用的“最佳”来源。这意味着浏览器报告的GPS功能或位置(通过Google位置服务)。由于每个设备不同,您将得到不同的结果。您是否拥有专用的GPS设备,最好使用3G(或更好的通信) - 您将获得最佳的位置结果。我认为这是一个错误,只是尝试将设备放置在当前连接的任何网络上的现实。
答案 5 :(得分:0)
试试这个: -
public class MainActivity extends Activity implements LocationListener{
protected LocationManager locationManager;
protected LocationListener locationListener;
protected Context context;
TextView txtLat;
String lat;
String provider;
protected String latitude,longitude;
protected boolean gps_enabled,network_enabled;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtLat = (TextView) findViewById(R.id.textview1);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
}
@Override
public void onLocationChanged(Location location) {
txtLat = (TextView) findViewById(R.id.textview1);
txtLat.setText("Latitude:" + location.getLatitude() + ", Longitude:" + location.getLongitude());
}
@Override
public void onProviderDisabled(String provider) {
Log.d("Latitude","disable");
}
@Override
public void onProviderEnabled(String provider) {
Log.d("Latitude","enable");
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.d("Latitude","status");
}
}
还要确保在清单文件中指定了以下权限: -
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
答案 6 :(得分:0)
如果您需要准确定位,请明确将位置提供商设置为读取GPS(是的,它会使用更多电池)。
criteria.setPowerRequirement(Criteria.POWER_LOW);
这将使用使用wifi信号的无源定位&amp;移动电话信号塔在您所在的地区,我认为这是它出错的地方!!
POWER_LOW使用您附近的任何wifi /手机信号的IP地理位置。
根据http://www.ipligence.com/geolocation,我的静态IP地址位于我实际位置以北170公里处。所以,如果你使用我的wifi信号进行定位,它将会偏离170公里。
甚至手机信号也只能在你所在地区的信号塔三角测量中定位,并且不是最准确的,因为它们相互重叠可以保持信号强度。在Android应用程序上玩过这个,我说我认为被动位置提供者的想法在理论上是好的,在实际中不适用于定位。
并且您的标准存在冲突:
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setPowerRequirement(Criteria.POWER_LOW);
ACCURACY_FINE表示使用GPS作为位置提供者(使用重电池) POWER_LOW表示使用低功率位置提供者(因此不要使用GPS)
答案 7 :(得分:0)
我建议您实施Location's API新LocationClient
。关于选择哪个提供商的所有算法都是为您完成的;由于融合提供商,它总是使用最好的提供商。它还使用其他传感器来获得最准确的位置。
答案 8 :(得分:0)
其实我有同样的问题。我不仅可以在代码中看到它,还可以在其他设备的行为中看到它。
我的HTC Sensation上的谷歌地图显示我上周的位置女巫恰好是我最后一次运行该应用程序,它是我手机上唯一使用位置的应用程序。
所以我得出的结论是,这笔现金是如何管理自己的驱动程序。
我通过忽略这些结果解决了这个问题。我始终记得我收到的最后一个新位置数据,并且知道在不相关时忽略它。