使用 AsyncTask 从错误的线程异常调用

Posted

技术标签:

【中文标题】使用 AsyncTask 从错误的线程异常调用【英文标题】:Called From Wrong Thread Exception with AsyncTask 【发布时间】:2012-05-22 19:30:19 【问题描述】:

即使我将它扔到 AsyncTask 的 DoInBackground 方法中,我也会不断收到 CalledFromWrongThreadException!谁能告诉我为什么以及如何解决它?

/**
     * Recognizes key words in responseTexts and runs the appropriate function
     * @param responseText
     */
    private void responseHandler(String responseText) 
        if(responseText.startsWith("Notes", 0)) 
            notes.add(responseText);
            new DownloadFilesTask().execute("WriteInternal" , null, null);
        
    

这是我的 AsyncTask 类

private class DownloadFilesTask extends AsyncTask<String, Void, String> 
    protected String doInBackground(String... command) 
    if(command[0].equals("WriteInternal") 
        //write to internal storage
    FileOutputStream fOut = null;
    OutputStreamWriter osw = null;
    try
        fOut = openFileOutput("notes.txt", Context.MODE_PRIVATE);
        osw = new OutputStreamWriter(fOut);
        for (int i = 0; i < notes.size(); i++)
            osw.write(notes.get(i) + "\n");
        
        osw.close();
        fOut.close();
     catch(Exception e) 
        e.printStackTrace(System.err);
    

    notesListView.setAdapter(arrayAdapter);  //refreshes the listView

       
else 
        sendCommand(command[0]);
        return null;

    

    protected void onProgressUpdate(Void... progress) 
    protected void onPostExecute(String result) 

这是我得到的堆栈跟踪:

05-22 15:14:20.370: E/androidRuntime(11730): FATAL EXCEPTION: AsyncTask #2
05-22 15:14:20.370: E/AndroidRuntime(11730): java.lang.RuntimeException: An error occured while executing doInBackground()
05-22 15:14:20.370: E/AndroidRuntime(11730):    at android.os.AsyncTask$3.done(AsyncTask.java:278)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at java.util.concurrent.FutureTask.run(FutureTask.java:137)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at java.lang.Thread.run(Thread.java:856)
05-22 15:14:20.370: E/AndroidRuntime(11730): Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
05-22 15:14:20.370: E/AndroidRuntime(11730):    at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:4039)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at android.view.ViewRootImpl.invalidateChild(ViewRootImpl.java:722)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:771)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at android.view.ViewGroup.invalidateChild(ViewGroup.java:4005)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at android.view.View.invalidate(View.java:8576)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at android.view.View.invalidate(View.java:8527)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at android.widget.AbsListView.resetList(AbsListView.java:1734)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at android.widget.ListView.resetList(ListView.java:502)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at android.widget.ListView.setAdapter(ListView.java:442)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at com.nelson.jarvisclientics.Notes$DownloadFilesTask.doInBackground(Notes.java:281)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at com.nelson.jarvisclientics.Notes$DownloadFilesTask.doInBackground(Notes.java:1)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at android.os.AsyncTask$2.call(AsyncTask.java:264)
05-22 15:14:20.370: E/AndroidRuntime(11730):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
05-22 15:14:20.370: E/AndroidRuntime(11730):    ... 5 more

【问题讨论】:

发布运行此代码时收到的堆栈跟踪也会很有帮助。 【参考方案1】:

尝试使用runOnUIThread(new Runnable/*code goes here*/()) 包装您的文件访问代码

【讨论】:

请注意:按照其他人所说的那样,使用 Android 的设计原则并在 onPostExecute() 中处理它可能会更好,更多。只需缓存您的文件,然后将其实际写入 onPostExecute()。 您不应该在 ui 线程上进行文件访问。 notesListView.setAdapter(arrayAdapter) 必须在 ui 线程上完成。但是使用 AsyncTask,您可以在 ui 线程上执行的 onPostExecute() 中执行此操作。 这解决了我遇到的问题,但我仍然不明白为什么。我在 onProgressUpdate (在 UI 线程上运行)中将元素添加到我的列表中并得到了这个异常,当我添加它时它停止了。【参考方案2】:

您收到此异常的原因是您尝试在 AsyncTask 的 doInBackground() 方法中更新 UI。当您在此处为列表视图设置适配器时会发生这种情况:notesListView.setAdapter(arrayAdapter); //refreshes the listView

您应该将其移至 onPostExecute。更新 onPostExecute 中的 UI 并在必要时显示任何错误消息。

还请注意,您也在后台调用 handleCommand();确保这也不会更新 UI!

【讨论】:

【参考方案3】:

您可以使用在 UI 线程上运行的 onPostExecute...这是使用 asynctask 的基础。

【讨论】:

我在上面的解决方案中添加了一条评论,表明这是更好的方法。

以上是关于使用 AsyncTask 从错误的线程异常调用的主要内容,如果未能解决你的问题,请参考以下文章

线程以未捕获的异常退出,AyncTask #2 致命错误

AsyncTask 坑 哪些线程可以调用AsyncTask

Android AsyncTask

无法在 AsyncTask 中为 ProgressDialog 调用 Looper.prepare() 的线程内创建处理程序

AsyncTask内的各个方法调用顺序

从 AsyncTask 更新视图