无法触及onPostExecute的AsyncTask中的视图

时间:2014-05-11 06:30:24

标签: android android-asynctask

我已经阅读了有关此内容的所有问题。而且情况并非如此。相信我。

我知道AsyncTask doInBackground函数有自己的线程,你必须触摸onPreExecute和onPostExecute中的视图。

此外,我使用isCancelled和isFinishing来确保任务是活的。所有这些东西,应用程序仍在崩溃。

这是代码:

public class MainActivity  {

    private Activity activity;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.Main);
        activity = this;
        new MyTask(activity).execute();
    }


    private class MyTask extends AsyncTask<Void, Void, User[]> {

            // ...

            @Override
            protected void onPreExecute() {
            }

            @Override
            protected User[] doInBackground(String... params) {
                    // Api call
                    // return results
            }

            @Override
            protected void onPostExecute(User[] users) {

                    if (isCancelled() || isFinishing()) return;

                    findViewById(R.id.panelViews).removeAllViews();
                    findViewById(R.id.element).setText("something");

                    // ... more

            }
    }

}

如你所见:

  • 触摸视图位于onPostExecute
  • 我使用&#34; isCancelled&#34;如果用户取消退出屏幕的任务
  • 与&#34;相同的是#Finishing&#34;

即使现在我收到错误&#34;只有创建视图层次结构的原始线程才能触及其视图&#34;在我尝试更改视图的某些内容时(在示例中为简单文本)

如果我在尝试获取API结果时取消(例如后退),则可以正常工作。 如果我确切地取消API结果的时间,并且在它开始更改之前,它会崩溃(我在onPostExecute中插入一个&#34; sleep&#34;来完成此测试)。

错误代码在第112行,即&#34; findViewById(R.id.panelViews).removeAllViews();&#34;如上所述:

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    at android.view.ViewRoot.checkThread(ViewRoot.java:2988)
    at android.view.ViewRoot.requestLayout(ViewRoot.java:648)
    at android.view.View.requestLayout(View.java:8416)
    at android.view.View.requestLayout(View.java:8416)
    at android.view.View.requestLayout(View.java:8416)
    at android.view.View.requestLayout(View.java:8416)
    at android.view.View.requestLayout(View.java:8416)
    at android.widget.ScrollView.requestLayout(ScrollView.java:1303)
    at android.view.View.requestLayout(View.java:8416)
    at android.view.View.requestLayout(View.java:8416)
    at android.view.ViewGroup.removeAllViews(ViewGroup.java:2354)
    at com.test.activities.MainActivity$MyTask.onPostExecute(MainActivity.java:112)
    at com.test.activities.MainActivity$MyTask.onPostExecute(MainActivity.java:1)
    at android.os.AsyncTask.finish(AsyncTask.java:417)
    at android.os.AsyncTask.access$300(AsyncTask.java:127)
    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:130)
    at android.os.HandlerThread.run(HandlerThread.java:60)

4 个答案:

答案 0 :(得分:2)

查看调用堆栈,以下调用表明AsyncTask类已加载到非UI线程上。

at android.os.HandlerThread.run(HandlerThread.java:60)

AsyncTask reference documentation中提到的线程规则之一是:

  

必须在UI线程上加载AsyncTask类。这个完成了   自JELLY_BEAN起。

以下是与此issue相关的错误。

其中一个解决方案是,在AsyncTask方法中手动加载Application onCreate类。这可确保在任何应用程序组件使用它之前,类在主线程上加载。

public class MyApplication extends Application {

    @Override
    public void onCreate() {

        try {
            Class.forName("android.os.AsyncTask");
        } catch (ClassNotFoundException e) {

        }

        super.onCreate();
    }
}

请务必在MyApplication文件中指定AndroidManifest

答案 1 :(得分:2)

this answer中的解决方案应该强制代码在ui线程上运行。它使用Activity#runOnUiThread(Runnable)函数。你的代码最终会像这样:

@Override
protected void onPostExecute(User[] users) {
    if (isCancelled() || isFinishing()) return;

    runOnUiThread(new Runnable() {

        @Override
        public void run() {
            findViewById(R.id.panelViews).removeAllViews();
            findViewById(R.id.element).setText("something");
            // ... more
        }
    });
}

答案 2 :(得分:1)

从后台线程启动AsyncTask时会发生这种情况。 onPreExecuteonPostExecute不会自动在主线程上运行,但实际上是AsyncTask启动的线程。

换句话说,如果您在execute上调用AsyncTask时已经在后台线程中,那么同一个线程就是调用onPostExecute方法的意思那个例外。

答案 3 :(得分:0)

试试这个,这应该解决你的问题,在runOnUiThread()中执行你的任务

@Override
protected void onPostExecute(User[] users) {

    runOnUiThread(new Runnable() {
        public void run() {

            if (isCancelled() || isFinishing()) return;

            findViewById(R.id.panelViews).removeAllViews();
            findViewById(R.id.element).setText("something");                 

        }//run
    }//runOnUiThread
}//onPostExecute