Android的AsyncTask线程限制?

我正在开发一个应用程序,我需要每次用户login到系统时更新一些信息,我也在电话中使用数据库。 对于所有这些操作(更新,从数据库中检索数据等),我使用asynchronous任务。 到目前为止,我不明白为什么我不应该使用它们,但是最近我经历过,如果我做了一些操作,我的一些asynchronous任务就停止在预执行上,不要跳到doInBackground。 这太离谱了,所以我开发了另一个简单的应用程序来检查什么是错的。 奇怪的是,当asynchronous任务的总数达到5时,我得到相同的行为,第6个人在预执行时停止。

在Activity / App上,android是否有asyncTasks的限制? 还是只是一些错误,应该报告? 有没有人遇到同样的问题,也许find了解决办法?

这里是代码:

简单地创build5个线程在后台工作:

private class LongAsync extends AsyncTask<String, Void, String> { @Override protected void onPreExecute() { Log.d("TestBug","onPreExecute"); isRunning = true; } @Override protected String doInBackground(String... params) { Log.d("TestBug","doInBackground"); while (isRunning) { } return null; } @Override protected void onPostExecute(String result) { Log.d("TestBug","onPostExecute"); } } 

然后创build这个线程。 它将进入preExecute并挂起(它不会去doInBackground)。

 private class TestBug extends AsyncTask<String, Void, String> { @Override protected void onPreExecute() { Log.d("TestBug","onPreExecute"); waiting = new ProgressDialog(TestActivity.this); waiting.setMessage("Loading data"); waiting.setIndeterminate(true); waiting.setCancelable(true); waiting.show(); } @Override protected String doInBackground(String... params) { Log.d("TestBug","doInBackground"); return null; } @Override protected void onPostExecute(String result) { waiting.cancel(); Log.d("TestBug","onPostExecute"); } } 

所有AsyncTasks都由共享(静态) ThreadPoolExecutor和LinkedBlockingQueue内部控制。 当你在一个AsyncTask上调用execute时, ThreadPoolExecutor会在将来准备好的时候执行它。

“我什么时候准备好了?” ThreadPoolExecutor行为由两个参数控制,即核心池大小最大池大小 。 如果当前活动的核心池大小线程less于新的作业,执行程序将创build一个新的线程并立即执行。 如果至less有核心池大小线程正在运行,它将尝试对作业进行排队并等待直到有空闲线程可用(即,直到另一个作业完成)。 如果无法对作业进行排队(队列可以有最大容量),它将创build一个新的线程(最大池大小的线程),以便作业运行。非核心闲置线程最终可以退役根据保持活动超时参数。

在Android 1.6之前,核心池大小为1,最大池大小为10.由于Android 1.6,核心池大小为5,最大池大小为128.两种情况下,队列大小均为10。 保持活动超时时间为2.3秒前10秒,此后为1秒。

考虑到这一切,现在就明白为什么AsyncTask只会执行5/6的任务。 第六项任务正在排队,直到完成其中一项任务。 这是为什么你不应该使用AsyncTasks进行长时间运行的一个非常好的理由 – 它会阻止其他的AsyncTasks运行。

为了完整doInBackground ,如果你重复了6次以上的任务(例如30次),你会看到6个以上的doInBackground将会进入doInBackground因为队列将变满,并且执行器被推动创build更多的工作线程。 如果你长期执行任务,你应该看到20/30变得活跃,10个仍在队列中。

@antonyt有正确的答案,但如果你正在寻求一个简单的解决scheme,那么你可以检查出针。

有了它,你可以定义一个自定义的线程池大小,而不像AsyncTask ,它可以在所有 Android版本上工作。 有了它,你可以说这样的事情:

 Needle.onBackgroundThread().withThreadPoolSize(3).execute(new UiRelatedTask<Integer>() { @Override protected Integer doWork() { int result = 1+2; return result; } @Override protected void thenDoUiRelatedWork(Integer result) { mSomeTextView.setText("result: " + result); } }); 

或者类似的东西

 Needle.onMainThread().execute(new Runnable() { @Override public void run() { // eg change one of the views } }); 

它甚至可以做更多。 在GitHub上检查一下。

更新 :由于API 19,核心线程池大小已更改,以反映设备上的CPU数量,最less为2,最多为4,而最大为CPU * 2 +1。

 // We want at least 2 threads and at most 4 threads in the core pool, // preferring to have 1 less than the CPU count to avoid saturating // the CPU with background work private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; 

还要注意,尽pipeAsyncTask的默认执行程序是串行的(一次执行一个任务,并且按照它们到达的顺序执行),但方法

 public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) 

你可以提供一个Executor来运行你的任务。 你可以提供THREAD_POOL_EXECUTOR引擎盖下的执行程序,但是没有序列化的任务,或者你甚至可以创build你自己的执行程序并在这里提供它。 但是,请仔细注意Javadocs中的警告。

警告:允许多个任务从一个线程池并行运行通常不是我们想要的,因为它们的操作顺序没有定义。 例如,如果这些任务用于修改任何共同的状态(例如由于button点击而写入文件),则不能保证修改的顺序。 如果没有仔细的工作,极less数情况下,较旧版本的数据可能会被较旧版本覆盖,从而导致数据丢失和稳定性问题。 这样的改变最好是连续执行的; 为了保证这样的工作被序列化,不pipe平台的版本如何,你都可以在SERIAL_EXECUTOR中使用这个函数。

还有一点需要注意的是,框架提供的执行程序THREAD_POOL_EXECUTOR及其串行版本SERIAL_EXECUTOR(这是AsyncTask的默认值)是静态的(类级别结构),因此跨应用程序进程在所有AsyncTask实例之间共享。