AsyncTask一直在等待?

我的一个活动中的一个按钮调用AsyncTask来更新ListView的SimpleCursorAdapter的底层Cursor。 每次单击按钮时,都会添加AsyncTask的新线程并完成任务(进入“等待”状态)。 如果我单击该按钮5次或更多次,则5个AsyncTasks最终会以“等待”状态坐在那里。 这是正常的还是某处有内存泄漏?

AsyncTask

private class updateAdapter extends AsyncTask { @Override protected Void doInBackground(Void... params) { // Open database connection if(_db == null || !_db.isOpen()) _db = new DatabaseWrapper(ActivityShowWOD.this).getWritableDatabase(); Cursor WODcursor; // Check if a wod_id is set if(_wod_id == -1) { // Grab filters from preferences and at the same time build SQLselection string SharedPreferences prefs = getSharedPreferences("Preferences", 0); String[] filterNames = getResources().getStringArray(R.array.filters_values); boolean[] filterValues = new boolean[filterNames.length]; String SQLselection = ""; for (int i = 0; i < filterNames.length; i++) { filterValues[i] = prefs.getBoolean(filterNames[i], false); // Build SQL query if(filterValues[i] == true) { SQLselection += filterNames[i] + " = 1 OR " + filterNames[i] + " = 0"; } else { SQLselection += filterNames[i] + " = 0"; } // Add an "AND" if there are more filters if(i < filterNames.length - 1) SQLselection += " AND "; } // Get all WODs matching filter preferences WODcursor = _db.query(DatabaseConstants.TBL_WORKOUTS, new String[] { DatabaseConstants.WORKOUTS_ID, DatabaseConstants.WORKOUTS_NAME, DatabaseConstants.WORKOUTS_NOTES, DatabaseConstants.WORKOUTS_CFID }, SQLselection, null, null, null, null); // Move the Cursor to a random position Random rand = new Random(); WODcursor.moveToPosition(rand.nextInt(WODcursor.getCount())); // Store wod_id _wod_id = WODcursor.getInt(WODcursor.getColumnIndex(DatabaseConstants.WORKOUTS_ID)); } else { // Get the cursor from the wod_id WODcursor = _db.query(DatabaseConstants.TBL_WORKOUTS, new String[] { DatabaseConstants.WORKOUTS_ID, DatabaseConstants.WORKOUTS_NAME, DatabaseConstants.WORKOUTS_NOTES, DatabaseConstants.WORKOUTS_CFID }, DatabaseConstants.WORKOUTS_ID + " = " + _wod_id, null, null, null, null); WODcursor.moveToFirst(); } // Store WOD information into class instance variables and close cursor _wod_cfid = WODcursor.getInt(WODcursor.getColumnIndex(DatabaseConstants.WORKOUTS_CFID)); _wod_name = WODcursor.getString(WODcursor.getColumnIndex(DatabaseConstants.WORKOUTS_NAME)); _wod_notes = WODcursor.getString(WODcursor.getColumnIndex(DatabaseConstants.WORKOUTS_NOTES)); WODcursor.close(); // Return all exercises pertaining to this WOD _excCursor = _db.query(DatabaseConstants.TBL_EXERCISES, new String[] { DatabaseConstants.EXERCISES_ID, DatabaseConstants.EXERCISES_EXERCISE, DatabaseConstants.EXERCISES_REPS, DatabaseConstants.EXERCISES_NOTES }, DatabaseConstants.EXERCISES_WOD_ID + " = " + _wod_id, null, null, null, DatabaseConstants.EXERCISES_ID + " ASC"); return null; } @Override protected void onPostExecute(Void result) { _adapter.changeCursor(_excCursor); _adapter.notifyDataSetChanged(); _WODlist.setOnItemClickListener(new WODlistClickListener()); } } 

我的onCreate中调用任务的代码(首次加载活动时):

 upAdapter = new updateAdapter().execute(); 

在onClickListener按钮中:

  // Reset wod_id _wod_id = -1; // Update the underlying SimpleCursorAdapter upAdapter = new updateAdapter().execute(); 

其中一个AsyncTask的堆栈跟踪(对所有这些都是相同的):

 Object.wait(long, int) line: not available [native method] Thread.parkFor(long) line: 1535 LangAccessImpl.parkFor(long) line: 48 Unsafe.park(boolean, long) line: 317 LockSupport.park() line: 131 AbstractQueuedSynchronizer$ConditionObject.await() line: 1996 LinkedBlockingQueue.take() line: 359 ThreadPoolExecutor.getTask() line: 1001 ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) line: 1061 ThreadPoolExecutor$Worker.run() line: 561 Thread.run() line: 1096 

引擎盖下的AsyncTask使用ThreadPoolExecutor 。 这些线程可能不会消失一点,因为过于频繁地创建和拆除这些线程是一种浪费。 如果你创建了更多的AsyncTasks你会发现它会停止创建新的线程并重新使用旧的线程。

更新以解决一些细节:

你会认为如果池中有空闲线程,它就不会创建新线程,但这并不完全正确。 我们的想法是,有一定数量的线程可用来继续处理异步任务。 这称为核心池大小。 在Android的AsyncTask案例中,他们似乎已将其设置为5.如果您查看ThreadPoolExecutor的文档,它会说:

当在方法execute(Runnable)中提交新任务时,并且运行的线程少于corePoolSize线程,即使其他工作线程处于空闲状态,也会创建一个新线程来处理该请求。

还有一个最大称为最大池大小。

@kabuko说的是真的,但我也认为在开始新任务之前取消任务是一个好习惯。 如果其中一项旧任务继续进行,你可能会有一些奇怪的行为。 更重要的是,在您的情况下,您不希望多次查询您的数据库,这将是无用的。 您可以在这样的方法中将调用包装到异步任务:

 AsyncDataLoading loaderTask = null; private void runTask(){ if (loaderTask!=null && loaderTask.getStatus().compareTo(Status.FINISHED)!=0) { loaderTask.cancel(true); } loaderTask = new AsyncDataLoading(); loaderTask.execute(); } 

在异步任务完成时禁用按钮并重新启用它也是一种很好的做法。

无论如何,这个解决方案不适合您的架构,我对您的代码知之甚少。 希望它无论如何都会有所帮助。