I have a service that needs to know the phone's location. The main activities of the service are carried out in a thread as follows (with processing stuff removed:
Semaphore locationAcquired = new Semaphore(1);
LocationFinder finder;
...
public void run() {
delaySeconds = 60;
Looper.prepare();
while (true) {
try {
finder.StartFinder();
locationAcquired.acquire();
// do some stuff...
} catch (InterruptedException e) {
if (isDestroy) {
Log.d(TAG, "Closing Monitor Thread");
break;
} // else just wake up and process the location
} catch (Exception e) {
e.printStackTrace();
}
} // end while
} // end run
The LocationFinder class implementation (again, slightly simplified):
package com.ksdagile.opengate;
import...
public class LocationFinder {
public static final int ONE_SECOND = 1000;
LocationListener locationListener;
String provider = LocationManager.PASSIVE_PROVIDER; // passive by default
LocationManager locationManager;
public Location currentLocation;
long updateSeconds;
private boolean isLooking = false;
OpenGateService openGateService;
public LocationFinder(LocationManager _lm, OpenGateService _openGateService) {
openGateService = _openGateService;
locationManager = _lm;
// initialize with whatever location might be available
currentLocation = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
// A new location update is received. Do something useful with it. In this case,
// we're sending the update to a handler which then updates the UI with the new
// location.
currentLocation = location;
String newLoc = String.format("Found new Location lat:%.2f long:%.2f", currentLocation.getLatitude(), currentLocation.getLongitude());
Log.d(getClass().getName(), newLoc);
openGateService.locationAcquired.release();
}
// simple implementations of onProvider<> etc.
};
}
public void SetProvider(boolean isActive) {
if (isActive)
provider = LocationManager.GPS_PROVIDER;
else
provider = LocationManager.PASSIVE_PROVIDER;
}
public void SetFrequency(long delay) {
updateSeconds = delay;
}
public void StartFinder() {
if (!isLooking) {
isLooking = true;
locationManager.requestLocationUpdates(provider, updateSeconds*ONE_SECOND, 10, locationListener);
Log.d(getClass().getName(), String.format("Request location from %s provider, every %d sec.", provider, updateSeconds));
} else
Log.d(getClass().getName(), "Location request running");
}
public void StopFinder() {
locationManager.removeUpdates(locationListener);
isLooking = false;
}
public boolean IsLocating() {
return isLooking;
}
}
My problem is that the onLocationChanged routine is not called, even though I know there are new readings. For example, when configured to read in Passive Mode, I run Waze and see myself moving. Is it possible that the call to onLocationChanged being blocked by the semaphore? If so, how do I get around this? I want to be able to change the parameters for requestLocationUpdate dynamically.
I think getting a tumbleweed badge is a very dubious honor, but here is the answer: Yes, the .acquire() call blocks the thread, and the onLocationChanged() method is not called.
The solution is to have a handler run a runnable, such as:
public void onLocationChanged(Location location) {
xxxService.locHandler.post(xxxService.locationRun);
}
where
Runnable locationRun = new Runnable() {
@Override
public void run() {
// handle stuff for new location
}
};
The general principle is that you send and handle events, rather than running a polling loop. If you need to handle a non-event, e.g. no new location read, then you set up a countdown timer.