5

I'm working for an Android app and implementing a ProgressBar by using AsyncTask class.

The problem is that on some devices, it causes "CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views." in onPostExecute. On those devices, the problem occurs 100%. On other devices, it works fine.

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);
    }

I don't understand why onPostExecute does not run on the UI thread, on those certain devices.

Next, I tried to call it with runOnUiThread, to make absolutely sure that it runs on the UI thread.

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

Even this did not solve the problem. The same exception still occurs.

From Log, I confirmed that Thread.currentThread().getId() is certainly different from the app's main activity's thread inside the handler.

I'm stuck. Any advice will be appreciated.

NOTE:I edited the sample code (not a real code) above to fix the wrong method name and missing "return string". I will add more information later.

4

3 回答 3

7

我看不出它本身有什么问题MyAsyncTask,但还有其他事情可能会出错。

启动异步任务

来自Android 文档

线程规则

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

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

您不会显示您通常在哪里实例化并执行任务,因此请确保您在 UI/主线程上已经存在的代码中执行此操作。请注意,上面的第一个要点可能会解释为什么这在某些设备上对您有效,而在其他设备上无效。

创建视图层次结构

消息告诉你

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

并且您假设这是因为您的异步任务(奇怪地)试图在后台线程上修改 UI。但是,您可能会收到此错误,因为异步任务修改了主线程上的 UI,但 UI ( ProgressBar) 一开始就没有正确创建。

有关如何在错误线程(主线程以外的任何线程)上错误地创建视图并得到相同错误的示例,请参阅此问题。

更多的

但是,我想看看您在哪里记录线程 ID,以及您获得的值。如果您查看我的前两个建议,但它们不能解决您的问题,那么我们可能需要更多信息。

您还提到了一个处理程序(?),但没有说明您如何或在哪里使用它。通常, usingAsyncTask消除了对 use 的需要Handler,所以我有点担心你可能会如何使用它。

更新

根据下面评论中的讨论,看起来这里的问题是这个问题中讨论的问题。一些可能在后台线程上运行的代码首先导致AsyncTask 加载。所需类加载的原始(果冻豆之前)实现AsyncTask发生在主线程上(如上面的线程规则中所述)。简单的解决方法是在主线程(例如 in Application#onCreate())上添加代码,强制提前确定类加载AsyncTask

Class.forName("android.os.AsyncTask");
于 2013-08-28T04:08:03.743 回答
1

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

于 2015-06-09T14:55:26.600 回答
0

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

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

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

并像这样调用 onPostExecute() :

 handler.sendEmptyMessage(MSG);
于 2013-08-28T02:23:41.870 回答