Reputation: 959
I'm implementing a location suggestion activity which populates suggestions from an external server as the user types in a text view. I'm using an AsyncTask to fetch suggestions each time the text in the text view changes. When a new letter is typed, we cancel the task that already exists and execute a new one. Most of the time doInBackground starts immediately after execute is called, but other times it can take a few seconds. (Once doInBackground starts, performance is fine.)
Set listener:
private void init() {
// respond to any text change
textView.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(final CharSequence s, int start, int b, int c) {
showSuggestions(s.toString());
}
});
}
Here we start a new task and cancel the previous one:
private void showSuggestions(String query) {
// suggestionsTask is an instance variable of the activity
if (suggestionsTask != null) {
suggestionsTask.cancel(true);
}
suggestionsTask = new AsyncTask<String, Void, List<Suggestion>>() {
@Override
protected void onPostExecute(List<Suggestion> resultList) {
// set suggestions with adapter - CHANGES STATE
}
@Override
protected List<Suggestion> doInBackground(String... query) {
// one local db call for recent searches - DOES NOT CHANGE STATE
// one network call to external server - DOES NOT CHANGE STATE
// return results
}
};
suggestionsTask.execute(query);
}
Is there a better threading mechanism to use for this? Do you know why there is a delay between execute and doInBackground?
Upvotes: 1
Views: 321
Reputation: 1002
From the AsyncTask reference:
A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns. To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)
So you're actually not canceling the doInBackground() step unless you're manually checking whether isCancelled() is set periodically in doInBackground. In most versions of Android, all AsyncTasks share a single thread, so one has to finish before the next can start. So that's the reason for your delay, but I don't have enough information (i.e., you didn't post the code) from your doInBackground() code to come up with a suggestion on where to check for isCancelled().
If being able to cancel the previous task not possible for some reason, you can also try making your AsyncTasks execute in parallel, using executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR as the same documentation suggests, but with what you're trying to do that seems like it could cause some frustrating threading issues which would probably be worse than the one you're having right now.
Upvotes: 2