¿AsyncTask sigue esperando?

Un botón en una de mis actividades llama a AsyncTask que actualiza el cursor subyacente para SimpleCursorAdapter de un ListView. Cada vez que hago clic en el botón, se agrega un nuevo hilo para el AsyncTask y la tarea se completa (pasa al estado 'wait'). Si hago clic en el botón 5 o más veces, 5 AsyncTasks termina allí sentado con el estado de 'espera'. ¿Es esto normal o tengo una pérdida de memoria en alguna parte?

La AsyncTask

private class updateAdapter extends AsyncTask<Void, Void, Void> { @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()); } } 

Y el código en mi onCreate que llama a la tarea (cuando se carga la actividad por primera vez):

 upAdapter = new updateAdapter().execute(); 

Y en el botón onClickListener:

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

Stacktrace de uno de los AsyncTask (es lo mismo para todos ellos):

 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 debajo de la campana utiliza un ThreadPoolExecutor . Esos hilos podrían no desaparecer un poco porque sería un desperdicio seguir creando y derribando esos hilos con demasiada frecuencia. Después de un tiempo, si crea más AsyncTasks , encontrará que dejará de crear nuevos hilos y volverá a usar los antiguos.

Actualizar para tratar algunos detalles:

Se podría pensar que si hay hilos libres en la agrupación, no crearía nuevos, pero esto no es exactamente cierto. La idea es que hay un cierto número de hilos que son útiles para tener alrededor de mantener el procesamiento de tareas asíncronas. Esto se conoce como el tamaño de la agrupación principal. En el caso de AsyncTask de Android, parecen haberlo establecido en 5. Si miras la documentación de ThreadPoolExecutor dice:

Cuando se envía una nueva tarea en el método ejecutar (Runnable) y se ejecutan menos de los subprocesos corePoolSize, se crea un nuevo subproceso para controlar la solicitud, incluso si otros subprocesos de trabajo están inactivos.

También hay un máximo apropiado llamado el tamaño máximo de la piscina.

Lo que @kabuko dice es cierto, pero también creo que es una buena práctica cancelar la tarea antes de comenzar una nueva. Usted podría tener algunos comportamientos extraños en caso de que una de las viejas tareas continúen. Más sobre, en su caso no desea consultar su db más de una vez, sería inútil. Usted podría envolver la llamada a la tarea asíncrona en un método como este:

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

También es una buena práctica desactivar el botón y volver a habilitarlo cuando se realiza la tarea asíncrona.

De todos modos esta solución no podría ser un buen ajuste para su arquitectura, no sé bastante sobre su código. Espero que ayude de todos modos.

  • Cómo burlar un objeto inyectado que no se declara en el módulo?
  • Para cada uno dentro de un para cada - Java
  • ¿Puede alguien explicar cómo SharedPreferences almacena un conjunto de cadenas
  • Opacidad incremental, que desea una opacidad constante Vista de imagen con Drawable
  • Daga: Inyectar campo en el pojo proporcionado
  • Reemplazar espacio a guión
  • Android JNI - devuelve el rendimiento de la cadena json
  • ¿Cuál es el propósito de la anotación Android @Widget?
  • ViewRootImpl: ViewPostImeInputStage processPointer 0 en OnItemClick de Listview en android
  • Desafío: Animación personalizada de ViewPager. Cambiar la altura de los elementos elegidos (Ver doble)
  • Información sobre la clase Movie para Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.