studio - manual de programacion android pdf
Android: Cancelar la tarea asincrónica (8)
Utilizo una tarea asíncrona para cargar una imagen y obtener algunos resultados.
Al subir la imagen, veo un diálogo de progreso, escrito en el método onPreExecute () como este:
protected void onPreExecute() {
uploadingDialog = new ProgressDialog(MyActivity.this);
uploadingDialog.setMessage("uploading");
uploadingDialog.setCancelable(true);
uploadingDialog.show();
}
Ok, cuando presiono el botón Atrás, obviamente el diálogo desaparece debido a setCancelable (verdadero).
Pero (obviamente) la tarea asincrónica no se detiene.
Entonces, ¿cómo puedo solucionar esto? Quiero cancelar tanto el diálogo como la tarea asincrónica cuando presiono el botón Atrás. ¿Algunas ideas?
EDIT: ENCONTRÓ LA SOLUCIÓN . VEA MI RESPUESTA A CONTINUACIÓN.
De SDK:
Cancelando una tarea
Una tarea puede cancelarse en cualquier momento invocando cancelar (booleano). La invocación de este método provocará que las llamadas posteriores a isCancelled () devuelvan verdadero.
Después de invocar este método, onCancelled (Object), en lugar de onPostExecute (Object) se invocará después de que doInBackground (Object []) regrese.
Para garantizar que una tarea se cancele lo más rápido posible, siempre debe verificar el valor de retorno de isCancelled () periódicamente desde doInBackground (Object []), si es posible (dentro de un bucle, por ejemplo).
Entonces su código es correcto para el oyente de diálogo:
uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
myTask.cancel(true);
//finish();
}
});
Ahora, como mencioné anteriormente de SDK, debe verificar si la tarea está cancelada o no, para eso debe verificar isCancelled () dentro del método onPreExecute ().
Por ejemplo:
if (isCancelled())
break;
else
{
// do your work here
}
La mayoría de las veces que utilizo AsyncTask, mi lógica empresarial está en una clase de negocios separada en lugar de estar en la interfaz de usuario. En ese caso, no podría tener un bucle en doInBackground (). Un ejemplo sería un proceso de sincronización que consume servicios y persiste datos uno tras otro.
Terminé entregando mi tarea al objeto de negocio para que pueda manejar la cancelación. Mi configuración es así:
public abstract class MyActivity extends Activity {
private Task mTask;
private Business mBusiness;
public void startTask() {
if (mTask != null) {
mTask.cancel(true);
}
mTask = new mTask();
mTask.execute();
}
}
protected class Task extends AsyncTask<Void, Void, Boolean> {
@Override
protected void onCancelled() {
super.onCancelled();
mTask.cancel(true);
// ask if user wants to try again
}
@Override
protected Boolean doInBackground(Void... params) {
return mBusiness.synchronize(this);
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
mTask = null;
if (result) {
// done!
}
else {
// ask if user wants to try again
}
}
}
public class Business {
public boolean synchronize(AsyncTask<?, ?, ?> task) {
boolean response = false;
response = loadStuff(task);
if (response)
response = loadMoreStuff(task);
return response;
}
private boolean loadStuff(AsyncTask<?, ?, ?> task) {
if (task != null && task.isCancelled()) return false;
// load stuff
return true;
}
}
Me gustaría mejorar el código. Cuando canaliza a aSyncTask, se llama aSyncTask
al onCancelled()
(método de devolución de llamada de aSyncTask
), y allí puede ocultar su progressBarDialog
.
Puede incluir este código también:
public class information extends AsyncTask<String, String, String>
{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(String... arg0) {
return null;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
this.cancel(true);
}
@Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
@Override
protected void onCancelled() {
Toast.makeText(getApplicationContext(), "asynctack cancelled.....", Toast.LENGTH_SHORT).show();
dialog.hide(); /*hide the progressbar dialog here...*/
super.onCancelled();
}
}
Pasé un tiempo descifrando esto, todo lo que quería era un simple ejemplo de cómo hacerlo, así que pensé en publicar cómo lo hice. Este es un código que actualiza una biblioteca y tiene un diálogo de progreso que muestra cuántos libros se han actualizado y cancela cuando un usuario cierra el diálogo:
private class UpdateLibrary extends AsyncTask<Void, Integer, Boolean>{
private ProgressDialog dialog = new ProgressDialog(Library.this);
private int total = Library.instance.appState.getAvailableText().length;
private int count = 0;
//Used as handler to cancel task if back button is pressed
private AsyncTask<Void, Integer, Boolean> updateTask = null;
@Override
protected void onPreExecute(){
updateTask = this;
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
updateTask.cancel(true);
}
});
dialog.setMessage("Updating Library...");
dialog.setMax(total);
dialog.show();
}
@Override
protected Boolean doInBackground(Void... arg0) {
for (int i = 0; i < appState.getAvailableText().length;i++){
if(isCancelled()){
break;
}
//Do your updating stuff here
}
}
@Override
protected void onProgressUpdate(Integer... progress){
count += progress[0];
dialog.setProgress(count);
}
@Override
protected void onPostExecute(Boolean finished){
dialog.dismiss();
if (finished)
DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY, Str.TEXT_UPDATECOMPLETED, Library.instance);
else
DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY,Str.TEXT_NOUPDATE , Library.instance);
}
}
Solo puede solicitar la cancelación pero no terminarla realmente. Vea esta answer .
Tuve un problema similar: esencialmente estaba obteniendo un NPE en una tarea asíncrona después de que el usuario destruyó la actividad. Después de investigar el problema en , adopté la siguiente solución:
volatile boolean running;
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
running=true;
...
}
public void onDestroy() {
super.onDestroy();
running=false;
...
}
Luego, compruebo "si se está ejecutando" periódicamente en mi código asíncrono. He puesto a prueba esto y ahora no puedo "romper" mi actividad. Esto funciona perfectamente y tiene la ventaja de ser más simple que algunas de las soluciones que he visto en SO.
crea algunas variables miembro en tu actividad como
YourAsyncTask mTask;
Dialog mDialog;
Úselos para su diálogo y tarea;
en OnPause () simplemente llame
if(mTask!=null) mTask.cancel();
if(mDialog!=null) mDialog.dismiss();
ENCONTRÉ LA SOLUCIÓN : agregué un oyente de acción antes de subir el archivo Dialog.show () de esta manera:
uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener(){
public void onCancel(DialogInterface dialog) {
myTask.cancel(true);
//finish();
}
});
De esa manera, cuando presiono el botón Atrás, el OnCancelListener anterior cancela el diálogo y la tarea. También puede agregar finish () si desea finalizar toda la actividad en la parte posterior presionada. Recuerde declarar su tarea asíncrona como una variable como esta:
MyAsyncTask myTask=null;
y ejecuta tu tarea asíncrona de esta manera:
myTask = new MyAsyncTask();
myTask.execute();