我进行了一些日志记录,以查看运行onPreExecute,doInBackground和onPostExecute的线程.我会看到这样的结果……
onPreExecute ThreadId: 1 doInBackground ThreadId: 25 onPostExecute ThreadId: 18
我相信主ui线程id是1,我希望onPre和onPost都在线程1上执行.我确保创建并从ui线程调用execute方法(例如在Activity的onCreate中).
另外需要注意的是,我注意到后来的异步任务将在与前一个异步任务onPostExecute方法相同的线程上运行其onPostExecute方法(在本例中为线程18).
现在为了解决这个问题,我在调用runOnUiThread的过程中将代码包装在我的onPostExecute方法中,但我认为这很糟糕,并且想要解决真正的问题.
我没有想法!任何人有任何见解?我很乐意回答任何可以帮助进一步调查的问题!
编辑:
有两种方法可以在代码中运行异步任务.我想知道这些例子中的后者是否会导致一些奇怪的事情发生?
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!!!
}
}
}
以上看起来像AsyncTask的使用,但我仍然看到这个问题即使在这样的简单情况下也会发生.下一个示例使用异步任务来运行其他异步任务.也许有些东西我不知道当异步任务被构造导致一些奇怪的行为时会发生什么?
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,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);
}
}
}
也许这里发生的事情只是非常奇怪,而且根本不是如何使用异步任务,无论哪种方式都可以很好地解决问题的根源.
如果您需要更多背景,请告诉我.
解决方法
幕后问题是第一次(当第一次加载应用程序时)来自looper线程的AsyncTask调用,该线程不是主UI线程.此调用将AsyncTask中的sHandler静态变量初始化为错误的线程ID,然后在随后的所有AsyncTask $onPostExecute()调用中使用此id.
为了解决这个问题,我在第一次应用程序加载时调用一个空的(do-nothing)AsyncTask,只是为了正确初始化AsyncTask.