Android runOnUiThread / AsyncTask无法解析CalledFromWrongThreadException

时间:2013-08-28 02:13:41

标签: android multithreading android-asynctask handler

我正在使用Android应用程序并使用AsyncTask类实现ProgressBar。

问题在于,在某些设备上,它会导致“CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能触及其视图。”在onPostExecute中。在这些设备上,问题发生在100%。在其他设备上,它工作正常。

public final class MyAsyncTask extends AsyncTask<String, Integer, String>
{
    private ProgressBar progress;
    private ListActivity activity;

    public MyAsyncTask(ListActivity activity, ProgressBar progress)
    {
        this.progress = progress;
        this.activity = activity;
    }

    protected void onPreExecute()
    {
        this.progress.setVisibility(view.VISIBLE);
    }

    protected String doInBackground(String[] arg0)
    {
        // getting xml via httpClient
        return string;
    }

    protected void onPostExecute(String result)
    {
        this.progress.setVisibility(view.GONE);
    }

我不明白为什么onPostExecute不能在这些特定设备上的UI线程上运行。

接下来,我尝试使用runOnUiThread调用它,以确保它在UI线程上运行。

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        ProgressBar progress = (ProgressBar)findViewById(R.id.some_view_progressbar);
        MyAsyncTask task = new MyAsyncTask(activity, progress);
        task.execute();
    }
} );

即使这样也无法解决问题。同样的异常仍然会发生。

从Log,我确认Thread.currentThread()。getId()肯定不同于app在处理程序中的主要活动的线程。

我被困住了。任何建议将不胜感激。

注意:我编辑了上面的示例代码(不是真正的代码)来修复错误的方法名称并缺少“返回字符串”。 我稍后会添加更多信息。

3 个答案:

答案 0 :(得分:7)

MyAsyncTask本身并没有看到任何错误,但还有其他问题可能出错。

启动AsyncTask

来自Android Docs

  

线程规则

     

此类必须遵循一些线程规则   正常工作:

     
      
  • 必须在UI线程上加载AsyncTask类。这是从JELLY_BEAN开始自动完成的。
  •   
  • 必须在UI线程上创建任务实例。
  •   必须在UI线程上调用
  • execute(Params ...)。
  •   
  • 不要手动调用onPreExecute(),onPostExecute(Result),doInBackground(Params ...),onProgressUpdate(Progress ...)。
  •   
  • 任务只能执行一次(如果尝试第二次执行,则会抛出异常。)
  •   

您没有显示正常实例化的位置,并执行任务,因此请确保您已在UI /主线程上已有的代码中执行此操作。请注意,上面的第一个要点可能解释了为什么这在某些设备上适用于您,而不是在其他设备上。

创建视图层次结构

消息告诉你

  

只有创建视图层次结构的原始线程才能触及其视图。

并且您假设这是因为您的异步任务(奇怪地)尝试修改后台线程上的UI。但是,您可能会收到此错误,因为异步任务会修改主线程上的UI,但首先无法正确创建UI(ProgressBar)。

See this question有关如何在错误的线程上错误地创建视图(线程以外的任何其他内容)的示例,并获得同样的错误。

更多

但是,我希望确切地看到您正在记录线程ID的 where ,以及您获得的值。如果您查看我的前两个建议,但它们无法解决您的问题,那么我们可能需要更多信息。

您还提到了Handler(?),但没有说明您使用它的方式或位置。通常情况下,使用AsyncTask无需使用Handler,因此我有点担心您可能会如何使用它。

更新

根据以下评论中的讨论,此处的问题似乎是the one discussed in this question。某些代码(可能在后台线程上运行)首先导致AsyncTask 加载AsyncTask的原始(pre-Jelly Bean)实现需要在主线程上进行类加载(如上面的线程规则中所述)。简单的解决方法是在主线程上添加代码(例如在Application#onCreate()中),强制AsyncTask的早期确定性类加载:

Class.forName("android.os.AsyncTask");

答案 1 :(得分:1)

确保仅从主线程调用aysnctask.execute()。

答案 2 :(得分:0)

在UI线程中编写处理程序并从onPostExecute调用处理程序。它将解决问题。

像这样的东西。在UI线程(主线程)中有一个处理程序:

handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        //run on UI Thread
     }
}; 

并像这样调用onPostExecute():

 handler.sendEmptyMessage(MSG);