Método get () de AsyncTask: ¿Existe algún escenario en el que realmente sea la mejor opción?
Después de responder a esta pregunta, tuve una duda sobre el sentido / utilidad de usar el método get () de la clase AsyncTask de Android.
public final Result get () Waits if necessary for the computation to complete, and then retrieves its result.
Básicamente, una solución síncrona a la clase AsyncTask, que bloquea (congela) la interfaz de usuario hasta que se finalizan las operaciones de fondo.
- Android Limit EditText sólo a la entrada entero
- Caso de interés en multithreading con int volátil
- No se encontró ningún identificador de recurso para el atributo 'paddingEnd' en Package 'android'
- Comunicación USB entre Android (modo accesorio) y Windows PC (host)
- Mediante la consulta Sqlite, obtenga una fecha de milisegundos almacenados
Con excepción de los propósitos de la prueba, e incluso en esos casos, no puedo realmente pensar en cualquier situación en donde es realmente una buena solución, pero puedo ser incorrecto, así que me siento curioso.
Si necesita que el usuario realmente espere hasta que finalice AsyncTask, puede mostrar un Dialog o ProgressDialog, teniendo el control de la interfaz de usuario en cada momento. Sé que no es exactamente lo mismo, pero IMHO es un enfoque mucho mejor que usar el método get()
.
- La aplicación de licencias de Android no funciona?
- ¿Cómo implementar la facturación en la aplicación en una aplicación de Android?
- Android: cómo acceder a la tabla de contactos de SIM con el SDK?
- Anulación de registro de GCM causando que la aplicación se bloquee
- Significado de android @override
- ¿Debo usar! = O == primero al implementar un if else para comparación
- No se puede convertir una representación en un objeto de clase java.lang.String
- Icono de la pestaña de cambio de Android
AsyncTask
no es la única forma de realizar operaciones de fondo. De hecho, la documentación dice que AsyncTask
sólo debe ser utilizado para las operaciones de tomar en la mayoría de unos pocos segundos. Así que si tienes tareas que tardan más tiempo, deben ser codificadas a través de clases que implementan la interfaz ejecutables . Tales tareas en otros subprocesos (no AsyncTask) bien pueden querer esperar a que termine una AsyncTask
, por lo que me parece que la idea de que no hay situaciones en las que se desee utilizar AsyncTask.get()
es falsa.
Actualización: En respuesta a un comentario, para hacer hincapié en que esto podría ser un uso válido de AsyncTask.get()
, lo siguiente es posible:
- Podría haber
AsyncTask
que se inicien desde el subproceso de interfaz de usuario, lo que podría implicar la comunicación a través de Internet, por ejemplo, cargar una página web o comunicarse con un servidor. Cualquiera que sean los resultados deAsyncTask
, algunos (o todos) de los resultados son necesarios para actualizar la pantalla. Por lo tanto unAsyncTask
con sudoInBackground
seguido poronPostExecute
en el hilo de interfaz de usuario tiene sentido. - Siempre que el subproceso de interfaz de usuario inicie un
AsyncTask
, coloca el objetoAsyncTask
en una cola, para el procesamiento adicional por un subproceso de fondo independiente una vez que los resultados están disponibles. - Para cada
AsyncTask
en la cola a su vez, el hilo de fondo utilizaAsyncTask.get()
para esperar a que finalice la tarea, antes de realizar el procesamiento adicional. Un ejemplo obvio de procesamiento adicional podría ser simplemente registrar todas las actividades deAsyncTask
a un servidor en Internet, por lo que tiene sentido hacer esto en segundo plano.
El siguiente código muestra lo que quiero decir. La aplicación llama a KickOffAsynctask(...)
siempre que quiera hacer un AsyncTask, y hay un hilo de fondo que recogerá automáticamente la tarea para el post-procesamiento una vez que la tarea esté completa.
public class MyActivity extends Activity { static class MyAsyncTaskParameters { } static class MyAsyncTaskResults { } Queue<MyAsyncTask> queue; // task queue for post-processing of AsyncTasks in the background BackgroundThread b_thread; // class related to the background thread that does the post-processing of AsyncTasks @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); queue = new ConcurrentLinkedQueue<MyAsyncTask>(); b_thread = new BackgroundThread(queue); b_thread.Start(); } void KickOffAsynctask(MyAsyncTaskParameters params) { MyAsyncTask newtask = new MyAsyncTask(); newtask.execute(params); synchronized(queue) { queue.add(newtask); } } static class MyAsyncTask extends AsyncTask<MyAsyncTaskParameters, Void, MyAsyncTaskResults> { @Override protected MyAsyncTaskResults doInBackground(MyAsyncTaskParameters... params) { MyAsyncTaskResults results = new MyAsyncTaskResults(); // do AsyncTask in background return results; } @Override protected void onPostExecute(MyAsyncTaskResults res){ // take required results from MyAsyncResults for use in the user interface } } static class BackgroundThread implements Runnable { // class that controls the post processing of AsyncTask results in background private Queue<MyAsyncTask> queue; private Thread thisthread; public boolean continue_running; public BackgroundThread(Queue<MyAsyncTask> queue) { this.queue=queue; thisthread = null; continue_running = true; } public void Start() { thisthread = new Thread(this); thisthread.start(); } @Override public void run() { try { do { MyAsyncTask task; synchronized(queue) { task = queue.poll(); } if (task == null) { Thread.sleep(100); } else { MyAsyncTaskResults results = task.get(); // post processing of AsyncTask results in background, eg log to a server somewhere } } while (continue_running); } catch(Throwable e) { e.printStackTrace(); } } } }
Actualización2 . Se me ha ocurrido otro posible uso válido de AsyncTask.get()
. El consejo estándar es no utilizar AsyncTask.get()
del hilo de interfaz de usuario, ya que hace que la interfaz de usuario se congele hasta que el resultado esté disponible. Sin embargo, para una aplicación donde stealth se necesita, que puede ser exactamente lo que se requiere. Entonces, ¿qué hay de la siguiente situación: James Bond entra en la habitación del hotel de Le Chiffre y sólo tiene un par de minutos para extraer todos los datos del teléfono del villano e instalar el virus de monitoreo. Él instala la aplicación proporcionada por Q y comienza a correr, pero escucha a alguien que viene por lo que tiene que ocultar. Le Chiffre entra en la habitación y coge su teléfono para hacer una llamada. Por unos segundos el teléfono parece un poco indiferente, pero de repente el teléfono se despierta y hace su llamada telefónica sin pensarlo. Por supuesto, la razón de la falta de respuesta era el hecho de que la aplicación de Q estaba funcionando. Tenía varias tareas que hacer, y algunas de ellas tenían que hacerse en un orden particular. La aplicación utiliza dos subprocesos para realizar el trabajo, es decir, el subproceso de interfaz de usuario y el subproceso de fondo único que procesa AsyncTask
. El hilo de interfaz de usuario estaba en el control general de todas las tareas, pero debido a que había que realizar algunas tareas antes de otras tareas, había puntos en la aplicación donde el hilo de interfaz de usuario utilizaba AsyncTask.get()
mientras esperaba que la tarea de fondo terminara: ).
El método get
nunca debe ser utilizado en el hilo de la interfaz de usuario, ya que (como notó) congelar su interfaz de usuario. Se debe utilizar en un hilo de fondo para esperar el final de su tarea, pero en general, tratar de evitar AsyncTask
tanto como sea posible ( ¿POR QUÉ? ).
Yo sugeriría una devolución de llamada o eventbus como un reemplazo.
Si ejecuta varias asyncTasks en paralelo, es posible que desee que algunas tareas esperen el resultado de otras.
Sin embargo, creo que deberías evitar ejecutar demasiadas asyncTasks en paralelo, porque ralentizará el dispositivo.
Usualmente uso la clase handler para hacer el truco (Loading media async). Me gusta:
public void setRightSongAdele(Song current) { RetriveCorrelateGenreSong rcgs = new RetriveCorrelateGenreSong(current); new Thread(rcgs).start(); } @SuppressLint("HandlerLeak") Handler updateRightAdeleHandler = new Handler() { @Override public void handleMessage(Message msg) { songNextText.setText(utils.reduceStringLength(rightSong.getTitle(), 15)); adeleNext.setImageBitmap(utils.getAlbumArtFromSong(rightSong.getPath(), getApplicationContext())); } };
Publico mi propia respuesta, ya que es lo que pienso en este momento y nadie me lo ha dado.
Mi curiosidad sigue viva, así que no voy a marcar esto como correcto. Si alguien responde brillantemente esta pregunta, marcaré su respuesta como correcta.
Mi respuesta es que en realidad no hay escenario donde el uso del método get()
AsyncTask es la mejor solución.
IMHO, usar este método es inútil. Lo que quiero decir es que siempre parece que hay una mejor solución.
Del mismo modo, diría que es semánticamente impactante, ya que llamar cambia su AsyncTask en un eficaz "SyncTask".
Como se destaca la documentación:
"Espera si es necesario para completar el cálculo, y luego recupera su resultado".
Por lo tanto, es obligatorio que evite llamar al subproceso de la interfaz de usuario, de lo contrario obtendrá la NetworkOnMainThreadException
. Lo utilizaría en un entorno de prueba que implica operaciones de red o para esperar otra tarea para terminar.
EDIT: Yo estaba equivocado acerca de NetworkOnMainThreadException
.
Parece como si AsyncTask.get () bloquea el hilo de llamada, donde AsyncTask.execute () no.
Es posible que desee utilizar AsyncTask.get () para casos de prueba en los que desee probar una determinada llamada de servicio Web, pero no es necesario que sea asíncrona y quiera controlar cuánto tarda en completarse. O en cualquier momento que desee probar contra su servicio web en una suite de pruebas.
La sintaxis es la misma que execute:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } } new DownloadFilesTask().get(5000, TimeUnit.MILLISECONDS);
Puede utilizarlo en el filtro del adaptador de AutoCompleteTextView.
private class Adapter extends BaseAdapter implements Filterable{ getFilter(){ return new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { List<String> suggestions = MyApiSuggestionsApi(constraint).get(); return packageAsFilterResults(suggestions); } } }
Tengo una aplicación para editar fotos, el usuario es capaz de abrir fotos de las URL.
Utilicé un AsyncTask para descargar la foto de la URL, durante este tiempo la interfaz de usuario debería ser bloqueada. Lo que hice fue mostrar un ProgressBar inicia el método get () de AsyncTask que ocultar el ProgressBar.
Parecía la solución mejor y más simple para mí.