【问题标题】:AsyncTasks do not get collected causing other AsyncTasks to not run未收集 AsyncTask 导致其他 AsyncTask 无法运行
【发布时间】:2025-12-22 19:40:06
【问题描述】:

我的应用使用了很多 AsyncTask。毕竟它是一个网络应用程序。当我跟踪调试选项卡时,我注意到每个 AsyncTask 都说在它后面运行,并且在 5 个 AsyncTasks 之后,我无法启动任何 AsyncTasks。我通过将执行程序更改为THREAD_POOL_EXECUTOR 来修复它,它允许池化 15 个线程。但是 AsyncTasks 仍然显示为正在运行。

AsyncTasks 中都有 InputStreams 和 BufferedReaders 来读取 JSON,但我从未在 Streamers 和 Readers 上调用 close() 方法。是这样吗,还是AsyncTask完成后无论如何都会被收集?

如果是这样,那为什么我不能在我的应用中运行超过 5 个 AsyncTask?

既然我悬赏了,我会更明确地解释这一点

AsyncTasks 都通过他们的方法。除了BasicNameValuePairs 不同之外,它们都以完全相同的方式构建。我 100% 确定代码中没有简单的错误。

这是其中一个 AsyncTask 的示例:

private class RunningEvent extends AsyncTask<Void, Void, Response> {

    @Override
    protected void onPreExecute() {
        if (Constants.isOnline(getApplicationContext())) {
            super.onPreExecute();
        } else {
            Toast.makeText(getApplicationContext(),
                    "No internet connection", Toast.LENGTH_LONG).show();
            return;
        }
    }

    @Override
    protected Response doInBackground(Void... empty) {
        HttpClient client = new DefaultHttpClient();
        HttpPost post = new HttpPost(URL);

        try {
            List<NameValuePair> values = new ArrayList<NameValuePair>(5);
            values.add(new BasicNameValuePair("tag", "eventRunning"));
            values.add(new BasicNameValuePair("userid", String
                    .valueOf(response.user.userid)));
            post.setEntity(new UrlEncodedFormEntity(values));

            HttpResponse httpresponse = client.execute(post);
            HttpEntity entity = httpresponse.getEntity();
            InputStream stream = entity.getContent();

            Log.i("MenuActivity",
                    "Input streamed, parsing Gson for existing events");
            Gson gson = new Gson();
            Reader reader = new InputStreamReader(stream);

            eventresponse = gson.fromJson(reader, Response.class);
            return eventresponse;
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("RunningEvent", "Error sending data to Server");
        }
        return null;
    }

    @Override
    protected void onPostExecute(Response result) {
        // TODO Auto-generated method stub
        super.onPostExecute(result);
        Log.i("MenuActivity", "Binding button");
        if (eventresponse != null) {
            if (eventresponse.success == 1) {
                eventresponse.user = response.user;
                bActivity.setOnClickListener(new OnClickListener() {

                    public void onClick(View arg0) {
                        Intent i = new Intent("com.xxx.xxx.EVENT");
                        i.putExtra("response", eventresponse);
                        running = false;
                        switcher.cancel(true);
                        MenuActivity.this.finish();
                        startActivity(i);
                    }

                });
            } else {
                bActivity.setText("Nieuw activity");
                bActivity.setOnClickListener(new OnClickListener() {

                    public void onClick(View arg0) {
                        Intent i = new Intent("com.xxx.xxx.NEWEVENT");
                        i.putExtra("response", response);
                        running = false;
                        switcher.cancel(true);
                        MenuActivity.this.finish();
                        startActivity(i);
                    }

                });
            }
        } else {
            Log.i("RunningEvent", "Response is null");
        }
    }

}

上面的例子是有时会作为第6个AsyncTask运行,它永远不会进入doInBackground()方法。我相信这是 SERIAL_EXECUTOR 的 5 Thread 限制。我通过将大多数AsyncTasks 放入THREAD_POOL_EXECUTOR 来“解决”这个问题,但这只是在避免它。

这些AsyncTasks 从未停止运行并堵塞Executor 的原因可能是什么?

【问题讨论】:

  • AsyncTasks 应该完整吗?我的意思是任务是否仍在正常运行,或者它们是否已完成并仍在继续?
  • 它们一直在 ADT 调试工具中运行。
  • 任务完成了吗,意思是onPostExecute()被调用了吗?
  • 他们都完成了他们的步骤并完成了,但他们永远不会被收集。

标签: java android garbage-collection android-asynctask threadpool


【解决方案1】:

android.os.AsyncTask 自带两个内置执行器。如果使用 SERIAL_EXECUTOR,则没有线程池,并且所有 AsyncTask 都以串行顺序一次执行一个。如果使用 THREAD_POOL_EXECUTOR(我想这就是您在问题中所指的),则最多允许并行执行 128 个 AsyncTask。

你在调试中看到的数字5是底层线程池的corePoolSize(AKA.THREAD_POOL_EXECUTOR),它与maximumPoolSize不同。查看AsyncTask source code,看看线程池是如何实现的:

private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;

... ...

/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 */
public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

查看ThreadPoolExecutor API 以查看调用此构造函数创建的默认线程池行为是什么。一般来说,corePoolSize 是保持在池中的线​​程数,即使它们是空闲的,除非设置了allowCoreThreadTimeOut

您在调试中看到的那 5 个 AsyncTask 实际上是在核心线程上,这些线程已完成并变得空闲但从未终止,您可以通过调用 ThreadPoolExecutor.allowCoreThreadTimeOut(boolean) 来更改此行为。


关于 SERIAL_EXECUTOR 的更多故事

我说SERIAL_EXECUTOR 不使用线程池,这不是真的。 SERIAL_EXECUTOR 确实是把真正的工作委托给 THREAD_POOL_EXECUTOR,但是使用 ArrayDeque 来控制下一个任务的提交(如果上一个任务完成就提交下一个任务),查看源码:

private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

因此,无论您使用 SERIAL_EXECUTOR 或 THREAD_POOL_EXECUTOR,线程池中始终显示 5 个核心线程,即使它们已完成并变得空闲。但是,核心线程数(由corePoolSize配置)并不是当前线程池中运行的线程数(由maximumPoolSize配置)。

【讨论】:

  • 这非常详细和规范,但它仍然没有回答最初的问题:为什么我不能在我的应用程序中运行超过 5 个 Asynctasks?他们显然没有完成,但所有代码都在运行。
  • 在多个AsyncTask执行过程中是否点击了动态绑定按钮bActivity(我假设这是一个按钮对象)?
  • 不,它们在 onCreate 方法中触发。你不明白。所有的 AsyncTask 都是一样的,但是在调用了 5 个之后它们就不运行了。
  • 你误解了我的问题,我不是在问你的 AsyncTask 是如何触发的。您的代码中的潜在问题在于 AysncTask.onPostExecute() 实现,取决于条件逻辑,您创建完成当前活动的按钮 MenuActivity.this.finish(); 如果您单击它,当前活动将被销毁,因此,它的所有运行和挂起AsyncTask 变得不可引用并在内存中创建黑洞。除此之外,我没有看到任何可能导致您的代码出现这种意外行为的故障点。
  • 现在我明白了。我对此没有意见。 sometimes 上面的代码作为第 6 个 AsyncTask 运行。它通常是第二个 AsyncTask。如果我单击并返回菜单,它应该检查我是否启动了“事件”,如果是,它应该转到不同的活动。但代码本身确实有效。