未收集 AsyncTask 导致其他 AsyncTask 无法运行

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

My app uses a lot of AsyncTasks. It is a web app after all. And when I keep track of the Debug tab, I notice every AsyncTask says running behind it and after 5 AsyncTasks, I can't start any AsyncTasks. I fixed it by changing the executor to THREAD_POOL_EXECUTOR which allows 15 threads to be pooled. But the AsyncTasks still show as running.

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

The AsyncTasks all have InputStreams in them and BufferedReaders in them to read the JSON, but I never call the close() method on the Streamers and Readers. Could this be it, or will the AsyncTask be collected after it's finished no matter what?

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

If that's the deal, then why can't I run more than 5 AsyncTasks in my app?

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

The AsyncTasks all go through their methods. All of them are built the same exact way, except with different BasicNameValuePairs. I am 100% sure there is no easy mistake made in the code.

这是其中一个 AsyncTask 的示例:

Here is an example of one of the AsyncTasks:

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 中解决"了这个问题,但这只是在避免它.

The example above is the sometimes gets runned as the 6th AsyncTask and it will never enter the doInBackground() method. I believe this is the 5 Thread limit of the SERIAL_EXECUTOR. I "fixed" the problem by putting most AsyncTasks in THREAD_POOL_EXECUTOR, but this is just avoiding it.

这些 AsyncTasks 永远不会停止运行并阻塞 Executor 的原因可能是什么?

What could be the reason that these AsyncTasks never stop running and clogging up the Executor?

推荐答案

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

android.os.AsyncTask come with two built-in executor. if using SERIAL_EXECUTOR, there is no threadpool and all AsyncTask get execute one at a time in serial order. if using THREAD_POOL_EXECUTOR (I suppose this is what you refer in the question), this allows up to maximum 128 AsyncTask get execute in parallel.

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

The number 5 you refer and see from debugging is the corePoolSize of underlying threadpool (AKA. THREAD_POOL_EXECUTOR), which is different from maximumPoolSize. check out AsyncTask source code and see how threadpool is implemented:

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.

Check out ThreadPoolExecutor API to see what is the default threadpool behavior created by calling this constructor. Generally speaking, corePoolSize is the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set.

你在调试中看到的那 5 个 AsyncTask 实际上是在核心线程上,它们已经完成并变得空闲但从未终止,你可以通过调用 ThreadPoolExecutor.allowCoreThreadTimeOut(boolean).

Those 5 AsyncTask you see in debug are actually on core threads which are finished and become idle but never terminated, you can alter this behavior by calling ThreadPoolExecutor.allowCoreThreadTimeOut(boolean).

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

I said SERIAL_EXECUTOR does not use threadpool, this is not true. SERIAL_EXECUTOR is indeed delegate the real work to THREAD_POOL_EXECUTOR, but using ArrayDeque to control the submission of next tasks (next task is submitted iff the previous task is finished), check out the source:

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配置).

So whatever you use SERIAL_EXECUTOR or THREAD_POOL_EXECUTOR, there are always 5 core threads shown in threadpool even they are finished and become idle. However, number of core thread (configured by corePoolSize) is not the number of threads (configured by maximumPoolSize) currently running in threadpool.

相关文章