AsyncTask的doInBackground方法没有运行

35

我在使用AsyncTask类时遇到了问题。似乎在创建4或5个任务后,我的任务就停止工作了。

我有两个活动,一个是MainActivity,它只有一个按钮,用于启动名为ImageActivity的第二个活动。

ImageActivity非常简单。它有一个onCreate方法,用于设置布局,然后它启动一个新的AsyncTask以从互联网加载图像。这在前几次运行中很好地工作。但是突然间停止工作了。每次都会运行onPreExecute方法,但不运行doInBackground方法。我尝试用睡眠循环来简化doInBackground, 但是发生了同样的事情。我无法理解这种行为,因为在onDestroy方法中,异步任务既被取消又被设置为null。因此,每次我启动一个新的ImageActivity时,也会创建一个全新的AsyncTask。

我通过按返回按钮重新创建ImageActivity和任务,然后点击MainActivity上的按钮来完成此操作。

大家有什么想法吗?我真的为这个问题苦恼。

更新:启动ImageActivity的代码(在按钮的onClickListener中)

Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
intent.setClassName(this, ImageActivity.class.getName());
startActivity(intent);
代码以上启动了这个活动。
    public class ImageActivity extends Activity {

    private AsyncTask<Void, Void, Void> task;

    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.main);

        task = new AsyncTask<Void, Void, Void>() {

            @Override
            protected void onPreExecute()
            {
                Log.d(TAG, "onPreExecute()");
            }

            @Override
            protected Void doInBackground(Void... params)
            {
                Log.d(TAG, "doInBackground() -- Here is the download");
                // downloadBitmap("http://mydomain.com/image.jpg")
                return null;
            }

            @Override
            protected void onPostExecute(Void res)
            {
                Log.d(TAG, "onPostExecute()");
                if(isCancelled()){
                    return;
                }
            }
        }.execute();
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        task.cancel(true);
    }
}

更新:

我已经尝试使用传统的线程和runOnUiThread方法的组合,似乎效果更好。现在线程每次都能运行。


请发布您的代码。您是如何调用它的?如果没有代码,我们无法提供帮助。 - Falmarri
遇到了完全相同的问题,在我的应用程序中启动多个AsyncTasks后,应用程序只执行到onPreExecute(),但不进入doInBackground()。现在正在尝试您的线程方法.. - Mathias Conradt
1
我遇到了同样的问题。奇怪的是,它在不同的设备上表现不同。它会在平板电脑(Android 4.0)上出现,但是在运行完全相同代码的手机(Android 2.3.4)上却没有这个问题。 - Hong
在我的情况下,doInBackground 能够正常工作,但其中的 Log.d() 却无法正常工作! - Ali Shakiba
7个回答

24

移除AsyncTask并使用传统的线程代替将其与runOnUiThread组合似乎可以解决问题,但我仍然没有找到AsyncTask不稳定的原因。

这是对我有效的代码:

public class ImageActivity extends Activity {

    private Thread worker;

    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.main);

        worker = new Thread(new Runnable(){

            private void updateUI(final List<Object> list)
            {
                if(worker.isInterrupted()){
                    return;
                }
                runOnUiThread(new Runnable(){

                    @Override
                    public void run()
                    {
                        // Update view and remove loading spinner etc...
                    }
                });
            }

            private List<Object> download()
            {
                // Simulate download
                SystemClock.sleep(1000);
                return new ArrayList<Object>();
            }

            @Override
            public void run()
            {
                Log.d(TAG, "Thread run()");
                updateUI(download());
            }

        });
        worker.start(); }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        worker.interrupt();
    }
}

1
谢谢,我有同样的问题(之前在SO上发布过,但没有解决问题),通过谷歌搜索来到了这里 :) - Ryan
5
这说明了为什么AsyncTask可能会失败,包括你和我在内:http://foo.jasonhudgins.com/2010/05/limitations-of-asynctask.html - Archie1986
只是分享一下我的想法,最近我遇到了AsyncTask的一些类似于巫术的行为。然而,在关闭后台已经运行的AsyncTask之后,我成功地解决了这个问题。虽然很奇怪,但对我来说确实有效。 - faizanjehangir
我的答案概述了失败的具体原因。https://dev59.com/d2855IYBdhLWcg3w6Y1q#20620623 - Heath Borders

7

我遇到了类似的问题。在SDK 11之前,您不能并行运行多个异步任务。请查看这里获取更多信息


6
我也遇到了这个问题。如果你使用 AsyncTask.execute,你的任务会在一个串行队列上运行(来自 Android 4.3 源代码):
当 AsyncTasks 首次引入时,它们是在单个后台线程上串行执行的。从 DONUT 开始,这被更改为一个线程池,允许多个任务并行操作。从 HONEYCOMB 开始,为了避免由于并行执行导致的常见应用程序错误,任务在单个线程上执行。
这与我看到的行为一致。我有一个 AsyncTaskdoInBackground 中弹出一个对话框,并阻塞直到对话框关闭。对话框需要自己的 AsyncTask 来完成。对话框的 AsyncTask.doInBackground 方法从未执行,因为原始的 AsyncTask 仍然被阻塞。
解决方案是在单独的 Executor 中执行第二个 AsyncTask

doInBackground() 不应该调用 UI 方法(例如,弹出对话框,正如您所提到的)。 - Andrew Mackenzie
我在主线程上显示了对话框,但它是由“doInBackground”触发的。 - Heath Borders
1
这是应该被接受的正确答案。(我刚刚对这个主题进行了自己的调查,并得出了相同的结论 - 分离 Executor) - Alexander Malakhov
所以如果我正确地阅读了这份文档,他们从串行执行转变为并行执行,然后又回到了串行执行?现在,如果我们需要同时执行两个或更多的AsyncTasks,我们需要使用多个执行器,每个执行器都在自己的线程上运行? - Joshua Pinter
是的,或者使用单个并行执行器。 - Heath Borders

1

使用 traceview 进行调查 -- 或获取线程转储。我猜测您的 AsyncTask 线程之一卡在下载上。

AsyncTask 有一个小的线程池,所以如果其中一个任务挂起,它可能会阻塞您的线程池。

这里有一个快速测试可以运行 -- 在4.3上,我看到我只能运行5个并发线程。当一个线程退出时,其他线程启动。

  private void testAsyncTasks() {

        for (int i = 1; i <= 10; i++) {
              final int tid = i;
              new AsyncTask<Integer, Void, Void>() {
                    protected void onPreExecute() {
                          Log.d("ASYNCTASK", "Pre execute for task : " + tid);
                    };

                    @Override
                    protected Void doInBackground(Integer... args) {
                          int taskid = args[0];
                          long started = SystemClock.elapsedRealtime();
                          Log.d("ASYNCTASK", "Executing task: " + taskid + " at " + started);
                          for (int j = 1; j <= 20; j++) {
                                Log.d("ASYNCTASK", "   task " + taskid + ", time=" + (SystemClock.elapsedRealtime() - started));
                                SystemClock.sleep(1000);
                          }
                          return null;
                    }

                    protected void onPostExecute(Void result) {
                          Log.d("ASYNCTASK", "Post execute for task : " + tid);
                    };
              }.execute(i);

        }
  }

0

我也遇到过这个问题,没有明显的原因无法启动。我注意到在重新启动adb之后它又可以工作了。不确定为什么会这样,但对我来说确实有效。


0

在Android中,你不必担心线程的管理问题,因为系统会自动处理。

请也提供图片下载的方法。你是否尝试过在onDestroy()方法中不取消线程?你是如何将图片返回到UI线程的?


downloadBitmap方法有点长,但这不是问题所在。如果我尝试模拟缓慢下载并使用Thread.sleep(5000),我会遇到同样的问题。当我使用Thread.sleep时,我可以启动ImageActivity 3-4次,任务就能正常工作。之后,任务只会打印onPreExecute而不是“doInBackground”日志消息,这意味着由于某种原因它从未被调用。是的,我已经尝试过不取消任务,但没有帮助。 - Vidar Vestnes
你还有省略的其他代码吗? - Octavian Helm

0
我认为问题出在繁重的图像下载任务上。即使您取消异步任务,图像下载仍将继续执行,并且异步任务直到下载完成才会结束。在下载进行时,您可能希望检查AyncTask上的isCancelled()方法,并在任务被取消时终止下载。
供参考,这是cancel()方法的文档: “尝试取消此任务的执行。如果任务已经完成、已经被取消或由于其他原因无法取消,则此尝试将失败。如果成功,并且在调用cancel时此任务尚未启动,则此任务不应运行。如果任务已经启动,则mayInterruptIfRunning参数确定是否应中断执行此任务的线程以尝试停止任务。 调用此方法将导致在doInBackground(Object[])返回后在UI线程上调用onCancelled(Object)。调用此方法保证永远不会调用onPostExecute(Object)。调用此方法后,您应定期检查从doInBackground(Object[])返回的isCancelled()的值,以尽早完成任务。”

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接