tutorial - servicios en segundo plano android studio
Android AsyncTask hilos lĂmites? (3)
@antonyt tiene la respuesta correcta, pero en caso de que esté buscando una solución simple, entonces puede consultar Needle.
Con él puede definir un tamaño de grupo de subprocesos personalizado y, a diferencia de AsyncTask
, funciona AsyncTask
en todas las versiones de Android. Con él puedes decir cosas como:
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);
}
});
o cosas como
Needle.onMainThread().execute(new Runnable() {
@Override
public void run() {
// e.g. change one of the views
}
});
Puede hacer mucho más. Compruébalo en GitHub .
Estoy desarrollando una aplicación donde necesito actualizar algo de información cada vez que el usuario inicia sesión en el sistema, también uso la base de datos en el teléfono. Para todas esas operaciones (actualizaciones, recuperación de datos de db, etc.) utilizo tareas asíncronas. Como hasta ahora no veía por qué no debería usarlos, recientemente experimenté que si hago algunas operaciones algunas de mis tareas asincrónicas simplemente se detienen en la ejecución previa y no saltan a doInBackground. Eso era demasiado extraño como para dejarlo así, así que desarrollé otra aplicación simple solo para verificar qué estaba mal. Y, por extraño que parezca, obtengo el mismo comportamiento cuando el recuento de tareas asíncronas totales alcanza 5, el sexto se detiene en la ejecución previa.
¿Android tiene un límite de asyncTasks en la actividad / aplicación? ¿O es solo un error y debería ser informado? ¿Alguien experimentó el mismo problema y tal vez encontró una solución alternativa?
Aquí está el código:
Simplemente crea 5 de esos hilos para trabajar en segundo plano:
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");
}
}
Y luego crea este hilo. Ingresará preExecute y colgará (no irá a 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");
}
}
Todos los AsyncTasks están controlados internamente por un ThreadPoolExecutor compartido (estático) y un LinkedBlockingQueue . Cuando llame execute
en un AsyncTask, el ThreadPoolExecutor
lo ejecutará cuando esté listo en algún momento en el futuro.
¿Cuándo estoy listo? el comportamiento de un ThreadPoolExecutor
está controlado por dos parámetros, el tamaño de la agrupación principal y el tamaño máximo de la agrupación . Si hay subprocesos de tamaño de grupo de núcleo actualmente activos y aparece un nuevo trabajo, el ejecutor creará un nuevo hilo y lo ejecutará inmediatamente. Si hay al menos subprocesos de tamaño de grupo principal en ejecución, intentará poner en cola el trabajo y esperar hasta que haya un subproceso inactivo disponible (es decir, hasta que se complete otro trabajo). Si no es posible poner en cola el trabajo (la cola puede tener una capacidad máxima), creará un nuevo subproceso (subprocesos de tamaño de agrupación máximo) para que se ejecuten los trabajos. Los subprocesos inactivos que no son núcleo pueden eventualmente retirarse. según un parámetro de tiempo de espera keep-alive.
Antes de Android 1.6, el tamaño del grupo principal era 1 y el tamaño máximo del grupo era 10. Desde Android 1.6, el tamaño del grupo principal es 5 y el tamaño máximo del grupo es 128. El tamaño de la cola es 10 en ambos casos. El tiempo de espera de mantener vivo fue 10 segundos antes de 2.3 y 1 segundo desde entonces.
Con todo esto en mente, ahora queda claro por qué AsyncTask
solo aparecerá para ejecutar 5/6 de sus tareas. La 6ta tarea se está poniendo en cola hasta que se complete una de las otras tareas. Esta es una muy buena razón por la que no debe usar AsyncTasks para operaciones de larga duración: evitará que otras AsyncTasks se ejecuten.
Para completar, si repitió su ejercicio con más de 6 tareas (por ejemplo, 30), verá que más de 6 ingresarán doInBackground
ya que la cola se doInBackground
y el ejecutor se verá obligado a crear más hilos de trabajo. Si se mantuvo con la tarea de ejecución prolongada, debería ver que 20/30 se active, y que 10 aún estén en la cola.
Actualización : desde la API 19, el tamaño del grupo de subprocesos de núcleo se modificó para reflejar la cantidad de CPU en el dispositivo, con un mínimo de 2 y un máximo de 4 al inicio, mientras crece a un máximo de CPU * 2 +1 - Reference
// 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;
También tenga en cuenta que si bien el ejecutor predeterminado de AsyncTask es serial (ejecuta una tarea a la vez y en el orden en que llegan), con el method
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params)
puede proporcionar un ejecutor para ejecutar sus tareas. Puede proporcionar THREAD_POOL_EXECUTOR bajo el ejecutor hood pero sin serialización de tareas, o incluso puede crear su propio ejecutor y proporcionarlo aquí. Sin embargo, tenga en cuenta la advertencia en los Javadocs.
Advertencia: Permitir que varias tareas se ejecuten en paralelo desde un grupo de subprocesos generalmente no es lo que uno quiere, porque el orden de su operación no está definido. Por ejemplo, si estas tareas se utilizan para modificar cualquier estado en común (como escribir un archivo debido a un clic de botón), no hay garantías sobre el orden de las modificaciones. Sin un trabajo cuidadoso es posible, en casos excepcionales, que la versión más nueva de los datos sea sobrescrita por una versión anterior, lo que lleva a problemas de estabilidad y pérdida de datos. Tales cambios se ejecutan mejor en serie; para garantizar que ese trabajo se serialice independientemente de la versión de la plataforma, puede usar esta función con SERIAL_EXECUTOR.
Una cosa más a tener en cuenta es que tanto el marco provisto por los ejecutores THREAD_POOL_EXECUTOR y su versión serial SERIAL_EXECUTOR (que es el predeterminado para AsyncTask) son estáticos (construcciones a nivel de clase) y por lo tanto compartidos en todas las instancias de AsyncTask en su proceso de aplicación.