Reputation: 1889
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.
Upvotes: 1
Views: 102
Reputation: 9375
I would store the last city location used by the application in a preference.
I would getLastLocation() in its own background thread every time the application starts/restarts (or to be more exact, every time the application resumes its ui thread) and I would store the resulting city in the preference I spoke of above. Since that method doesn't power up the antenna and doesn't drain the battery, I wouldn't have any problem doing it that frequently.
And in answer to your original question in your title, no, "waiting on a thread doesn't defeat the purpose of using a thread". The term "waiting" in that context can be confusing. "Waiting" for a result to show up and rendering a perfectly working UI in the meantime is not the same as the UI Thread "waiting" for a result before it can draw a single pixel and freezing everything out from the UI until that result has come in. So in that sense, it doesn't matter if you're using two different async background threads as long as they're forked off from the initial UI thread (and not nested).
And then, the only code I'd place in the onclicklistener would be an async task that displays a progress spinning wheel and that does the getgpslocation in the background to insert the value into the preference, and triggers the content refresh.
Upvotes: 1
Reputation: 3910
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.
Upvotes: 1
Reputation: 4567
You can use LocationListener. In this way your code will run as soon as the new location is available and you won't have to block a thread.
Read more: http://developer.android.com/reference/android/location/LocationListener.html
Upvotes: 1