This is something that has always confused me a bit about threads.
I've got a thread that uses a LocationManager to find the Android device's current location. I then update the UI to show that location.
In fact, I've built an entire class called LocationFinder to get the location for me. That way I can say
String yourLocation = (new LocationFinder).getLocation();
However, the getLocation() method in LocationFinder runs on a separate thread. So I cannot actually say
String yourLocation = (new LocationFinder).getLocation();
because the immediate value that is returned will surely not be the location, as the location takes several fractions of a second to find. getLocation() defaults to return "notset" until an interior method sets the return value to the actual location.
Anyway, I'm baffled on how to approach this. I don't want to block until the location is found, because it's incredibly annoying for the app to lock up for those few milliseconds. I don't want to use AsyncTask, because that's already used when I call getLocation(), and I feel it would be a mistake to have nested AsyncTasks.
Here's the psuedocode for this hierarchy:
public class MainActivity extends Activity {
Button locationButton = new Button(locationButtonClickListener);
LocationFinder locationFinder = new LocationFinder();
OnClickListener locationButtonClickListener = new OnClickListener() {
locationButton.setText(locationFinder.getLocation());
}
}
public class LocationFinder {
String city = "notYetSet";
public String getLastLocation() {
(new LastKnownLocationFinder).execute();
return city;
}
public String getGPSLocation() {
(new GPSLocationFinder).execute();
return city;
}
private class LastKnownLocationFinder extends AsyncTask {
protected String doInBackground {
city = [lots of code to get last known location];
}
}
private class GPSLocationFinder extends AsyncTask {
protected String doInBackground {
city = [lots of code to get location using GPS];
}
}
}
Hopefully that illustrates what I'm getting at.
Thank you.
Threads are there to divide the work up and most of the time run it parallel (asynchronous), there is pretty much no other reason for them. In some cases, one would need one thread to finish something for the second thread to deal with the result (synchronous).
Now, if you are aware that 100% of the time the work of both threads are sequential, then, theoretically, there is no need have two threads. Except sometimes people would want to encapsulate different work in different components.
As for the Android, the application starts with one thread (UI-thread / Main-thread) which is intended for controlling all the UI element changes, and should never be blocked or put to sleep or have long operations take effect on it, and that is to reduce any lag or unresponsiveness experienced by the user (which is what you seem to be doing currently).
In your case, everything seems to be done right, except for one thing, from what I can understand in your psuedocode, getLocation()
will always return "notset" when called because there isn't enough time for the AsyncTask to fully execute and change the value.
You should build an interface
to communicate between the LocationFinder
and MainActivity
to send the location signal once found, even possibly just send the location right away.
public class LocationFinder {
private CommunicateLocationFinder mCommunicate;
public interface CommunicateLocationFinder {
void LocationFinderSendGpsLocation(String location);
}
public LocationFinder (Activity activity){
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCommunicate = (CommunicateLocationFinder) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement CommunicateLocationFinder");
}
}
private class GpsLocationFinder extends AsyncTask {
protected String doInBackground {
/*
* Your work here
*/
}
protected void onPostExecute (String result){
mCommunicate.LocationFinderSendGpsLocation(result);
}
}
/*
* Rest of your code
*/
}
public class MainActivity extends Activity implements
LocationFinder.CommunicateLocationFinder {
@Override
public void LocationFinderSendGpsLocation(String location){
locationButton.setText(location);
}
/*
* Rest of your code
*/
}
I hope that helps, let me know, cheers.