Android AsyncTask onPostExecute脱离主ui线程 [英] Android AsyncTask onPostExecute off of main ui thread

查看:88
本文介绍了Android AsyncTask onPostExecute脱离主ui线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在AsyncTask和onPostExecute上遇到问题.我发现onPostExecute在与主ui线程不同的线程上执行,这导致在我修改任何视图时发生CalledFromWrongThreadException.

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.

我进行了一些日志记录,以查看onPreExecute,doInBackground和onPostExecute在哪些线程上运行.我会看到这样的结果...

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

我相信主ui线程ID为1,并且我希望onPre和onPost都在线程1上执行.我确保从ui线程创建并调用execute方法(例如,在onCreate的onCreate中).活动).

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).

我要注意的另一件事是,更高版本的异步任务将与先前的异步任务onPostExecute方法(在本例中为线程18)在同一线程上运行其onPostExecute方法.

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).

现在为了解决这个问题,我在调用runOnUiThread的过程中将代码包装在onPostExecute方法中,但是我认为这很棘手,并且想解决真正的问题.

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!

有两种方法可以在代码中运行异步任务.我想知道这些示例中的后者是否引起了奇怪的事情发生?

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!!!
        }
    }

}

以上内容似乎是AsyncTask的原始用法,但即使在这样的简单情况下,我仍然看到此问题的发生.下一个示例使用异步任务来运行其他异步任务.也许我不知道当构造异步任务并导致某些奇怪行为时会发生什么?

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.

推荐答案

我遇到了同样的问题,结果发现问题出在使用Flurry 3.2.1.但是,问题不仅限于Flurry库.

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.

幕后问题是有史以来第一次(首次加载应用程序时)来自不是主UI线程的循环程序线程的AsyncTask调用.此调用将AsyncTask中的sHandler静态变量初始化为错误的线程ID,然后在所有后续的AsyncTask $ onPostExecute()调用中使用此ID.

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.

为解决此问题,我在首次加载应用程序时调用了一个空(不执行任何操作)AsyncTask,只是为了正确初始化AsyncTask.

To solve the problem, I call an empty (do-nothing) AsyncTask on first app load, just to initialize AsyncTask correctly.

这篇关于Android AsyncTask onPostExecute脱离主ui线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆