我正在使用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在处理程序中的主要活动的线程。
我被困住了。任何建议将不胜感激。
注意:我编辑了上面的示例代码(不是真正的代码)来修复错误的方法名称并缺少“返回字符串”。 我稍后会添加更多信息。
答案 0 :(得分:7)
MyAsyncTask
本身并没有看到任何错误,但还有其他问题可能出错。
线程规则
此类必须遵循一些线程规则 正常工作:
- 必须在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);