I'm having an issue with AsyncTask and onPostExecute. I am finding that onPostExecute is executing on a different thread than the main ui thread, which is causing a CalledFromWrongThreadException to happen when I modify any views.
I put in some logging to see what threads onPreExecute, doInBackground, and onPostExecute are running on. I would see a result like this...
onPreExecute ThreadId: 1
doInBackground ThreadId: 25
onPostExecute ThreadId: 18
I believe the main ui thread id is 1 and I would expect both onPre and onPost to both execute on thread 1. I am making sure to create and also call the execute method from the ui thread (for example in onCreate of an Activity).
Another thing to note that I have noticed is that later async tasks will run their onPostExecute method on the same thread as previous async task onPostExecute methods (in this case thread 18).
Right now in order to get around this I am wrapping the code in my onPostExecute methods in a call to runOnUiThread, but I think this is hacky and would like to get to the real issue.
I am out of ideas! Any one have any insight? I'm happy to answer any questions that could helper with further investigation!
EDIT:
There are two ways that async tasks are being run in the code. I am wondering if the latter in these examples is causing something weird to happen?
public class SomeActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
new SomeAsyncTask().execute();
}
private class SomeAsyncTask extends AsyncTask<String, Void, Integer> {
@Override
public void onPreExecute() {
Thread.currentThread().getId() // 1
//Show a dialog
}
@Override
public Integer doInBackground(String... params) {
Thread.currentThread().getId() // 25
return 0;
}
@Override
public void onPostExecute(Integer result) {
Thread.currentThread().getId() // 18
//hide dialog
//update text view -> CalledFromWrongThreadException!!!
}
}
}
The above seems like a vanilla use of AsyncTask, but I still see this issue occurring even in simple cases like this. The next example uses an async task to run other async tasks. Maybe there is something I don't know about what happens when an async task gets constructed that is causing some weird behavior?
public class SomeActivity extends Activity implements TaskRunner.OnFinishListener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
TaskRunner taskRunner = new TaskRunner();
taskRunner.setOnFinishListener(this);
taskRunner.addTask(new SingleTask());
taskRunner.addTask(new SingleTask());
taskRunner.execute();
}
@Override
public void onTaskFinish(List<Integer> results) {
//Thread id is 18 when it should be 1
//do something to a view - CalledFromWrongThreadException!!
}
}
//In a different file
public class SingleTask extends AsyncTask<String, Void, Integer> {
//This is a an async task so we can run it separately as an asynctask
//Or run it on whatever thread runnerExecute is called on
@Override
public Integer doInBackground(String... params) {
return runnerExecute(params);
}
//Can be called outside of doInBackground
public Integer runnerExecute(String... params) {
//some long running task
return 0;
}
}
//In a different file
public class TaskRunner {
private List<SingleTask> tasks;
private OnFinishListener onFinishListener;
public interface OnFinishListener {
public void onTaskFinish(List<Integer> results);
}
public TaskRunner() {
this.tasks = new ArrayList<SingleTask>();
}
public void setOnFinishListener(OnFinishListener listener) {
this.onFinishListener = listener;
}
public void addTask(SingleTask task) {
tasks.add(task);
}
public void executeTasks() {
new RunnerTask().execute((SingleTask[]) tasks.toArray());
}
//Calls the runnerExecute method on each SingleTask
private class RunnerTask extends AsyncTask<SingleTask, Integer, List<Integer>> {
@Override
public void onPreExecute() {
//Runs on thread 1
}
@Override
public List<Integer> doInBackground(SingleTask... params) {
//Runs on arbitrary thread
List<Integer> results = new ArrayList<Integer>();
for(SingleTask task : params) {
int result =task.runnerExecute(task.getParams());
results.add(result);
}
return results;
}
@Override
public void onPostExecute(List<Integer> results) {
//Runs on thread 18
onFinishListener.onTaskFinish(results);
}
}
}
Maybe what is going on here is just super weird, and not at all how async tasks are meant to be used, either way it would be nice to get to the bottom of the issue.
Let me know if you need any more context.
I have been experiencing the same problem and it turned out the the issue was using Flurry 3.2.1. However, the issue is not limited to the Flurry library.
The issue behind the scenes is having the first ever (when the app is loaded for the first time) AsyncTask call from a looper thread which is not the Main UI thread. This call initializes a sHandler static variable in AsyncTask to the wrong thread id, and this id is then used in all subsequent AsyncTask$onPostExecute() calls.
To solve the problem, I call an empty (do-nothing) AsyncTask on first app load, just to initialize AsyncTask correctly.
try using:
getBaseContext().runOnUiThread(new Runnable()
{
@override
public void run()
{
}
});
and write your code inside the run
function
The AsyncTask is designed to be used from the main thread. Your problem is the second case, and is that you call execute on the SingleTask from a background thread. You call it in the doInBackground method of RunnerTask. The onPostExecute is then run from the backgroundthread of RunnerTask
Two options for you.
1: Trash RunnerTask, and execute the SingleTasks from you main thread, they'll all run in parallell and you won't know which finishes first, but onPreExecute and onPostExecute is called on the main thread
2: Trash the SingleTask and define them as Runnables instead, then you can run them in sequence in the RunnerTask's doInBackground. They'll all run in the background thread of RunnerTask, in the order you call Run. When it is finished, the onPostExecute of RunnerTask is run on the main thread.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With