Avec Fedor est la solution que j'ai connu de multiples exécution de la fonction de rappel gotLocation
.
Il semble être due à une condition de concurrence dans le substituée LocationListener.onLocationChanged
méthode, lorsque gotLocation méthode est 'assez longtemps'. Je ne suis pas sûr, mais je suppose removeUpdates
empêche la enqueueing de nouveaux messages dans l'Arpenteuse de la file d'attente, mais il n'est pas supprimer ceux déjà mis en file d'attente, mais pas encore consommé. D'où la condition de la course.
Pour réduire la probabilité de ce mauvais comportement, il est possible de faire appel removeUpdates avant le déclenchement de la onLocationChanged événement, mais nous avons toujours la condition de la course.
La meilleure solution que j'ai trouvé est de remplacer requestLocationUpdates
avec requestSingleUpdate
.
C'est ma version, basée sur Fedor est la solution, en utilisant un Gestionnaire d'envoyer un message au fil du boucleur:
public class LocationResolver {
private Timer timer;
private LocationManager locationManager;
private LocationResult locationResult;
private boolean gpsEnabled = false;
private boolean networkEnabled = false;
private Handler locationTimeoutHandler;
private final Callback locationTimeoutCallback = new Callback() {
public boolean handleMessage(Message msg) {
locationTimeoutFunc();
return true;
}
private void locationTimeoutFunc() {
locationManager.removeUpdates(locationListenerGps);
locationManager.removeUpdates(locationListenerNetwork);
Location networkLocation = null, gpsLocation = null;
if (gpsEnabled)
gpsLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (networkEnabled)
networkLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
// if there are both values use the latest one
if (gpsLocation != null && networkLocation != null) {
if (gpsLocation.getTime() > networkLocation.getTime())
locationResult.gotLocation(gpsLocation);
else
locationResult.gotLocation(networkLocation);
return;
}
if (gpsLocation != null) {
locationResult.gotLocation(gpsLocation);
return;
}
if (networkLocation != null) {
locationResult.gotLocation(networkLocation);
return;
}
locationResult.gotLocation(null);
}
};
private final LocationListener locationListenerGps = new LocationListener() {
public void onLocationChanged(Location location) {
timer.cancel();
locationResult.gotLocation(location);
locationManager.removeUpdates(this);
locationManager.removeUpdates(locationListenerNetwork);
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
private final LocationListener locationListenerNetwork = new LocationListener() {
public void onLocationChanged(Location location) {
timer.cancel();
locationResult.gotLocation(location);
locationManager.removeUpdates(this);
locationManager.removeUpdates(locationListenerGps);
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
public void prepare() {
locationTimeoutHandler = new Handler(locationTimeoutCallback);
}
public synchronized boolean getLocation(Context context, LocationResult result, int maxMillisToWait) {
locationResult = result;
if (locationManager == null)
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
// exceptions will be thrown if provider is not permitted.
try {
gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch (Exception ex) {
}
try {
networkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch (Exception ex) {
}
// don't start listeners if no provider is enabled
if (!gpsEnabled && !networkEnabled)
return false;
if (gpsEnabled)
locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, locationListenerGps, Looper.myLooper());
//locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps);
if (networkEnabled)
locationManager.requestSingleUpdate(LocationManager.NETWORK_PROVIDER, locationListenerNetwork, Looper.myLooper());
//locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
timer = new Timer();
timer.schedule(new GetLastLocationTask(), maxMillisToWait);
return true;
}
private class GetLastLocationTask extends TimerTask {
@Override
public void run() {
locationTimeoutHandler.sendEmptyMessage(0);
}
}
public static abstract class LocationResult {
public abstract void gotLocation(Location location);
}
}
J'utilise cette classe à partir d'une mesure de boucleur, comme le suivant:
public class LocationGetter {
private final Context context;
private Location location = null;
private final Object gotLocationLock = new Object();
private final LocationResult locationResult = new LocationResult() {
@Override
public void gotLocation(Location location) {
synchronized (gotLocationLock) {
LocationGetter.this.location = location;
gotLocationLock.notifyAll();
Looper.myLooper().quit();
}
}
};
public LocationGetter(Context context) {
if (context == null)
throw new IllegalArgumentException("context == null");
this.context = context;
}
public synchronized Coordinates getLocation(int maxWaitingTime, int updateTimeout) {
try {
final int updateTimeoutPar = updateTimeout;
synchronized (gotLocationLock) {
new Thread() {
public void run() {
Looper.prepare();
LocationResolver locationResolver = new LocationResolver();
locationResolver.prepare();
locationResolver.getLocation(context, locationResult, updateTimeoutPar);
Looper.loop();
}
}.start();
gotLocationLock.wait(maxWaitingTime);
}
} catch (InterruptedException e1) {
e1.printStackTrace();
}
if (location != null)
coordinates = new Coordinates(location.getLatitude(), location.getLongitude());
else
coordinates = Coordinates.UNDEFINED;
return coordinates;
}
}
où les Coordonnées est une classe simple avec deux propriétés: la latitude et la longitude.