La API AsyncTask está obsoleta en Android 11. ¿Cuáles son las alternativas?

Google está desaprobando la API AsyncTask de Android en Android 11 y sugiere usarla en su java.util.concurrentlugar. Puedes consultar el compromiso aquí .

 *
 * @deprecated Use the standard <code>java.util.concurrent</code> or
 *   <a href="https://developer.android.com/topic/libraries/architecture/coroutines">
 *   Kotlin concurrency utilities</a> instead.
 */
@Deprecated
public abstract class AsyncTask<Params, Progress, Result> {

Si mantiene un código base antiguo con tareas asincrónicas en Android, es probable que tenga que cambiarlo en el futuro. Mi pregunta es cuál debería ser el reemplazo adecuado del fragmento de código que se muestra a continuación usando java.util.concurrent. Es una clase interna estática de una Actividad. Estoy buscando algo que funcione conminSdkVersion 16

private static class LongRunningTask extends AsyncTask<String, Void, MyPojo> {
        private static final String TAG = MyActivity.LongRunningTask.class.getSimpleName();
        private WeakReference<MyActivity> activityReference;

        LongRunningTask(MyActivity context) {
            activityReference = new WeakReference<>(context);
        }

        @Override
        protected MyPojo doInBackground(String... params) {
            // Some long running task
            
        }

        @Override
        protected void onPostExecute(MyPojo data) {

            MyActivity activity = activityReference.get();
            activity.progressBar.setVisibility(View.GONE);
            populateData(activity, data) ;
        }     


    }
Answer
private WeakReference<MyActivity> activityReference;

Que bueno que esté en desuso, porque WeakReference<Context>siempre fue un truco y no una solución adecuada .

Ahora las personas tendrán la oportunidad de desinfectar su código.


AsyncTask<String, Void, MyPojo> 

Basado en este código, Progressen realidad no es necesario, y hay una Stringentrada + MyPojosalida.

En realidad, esto es bastante fácil de lograr sin el uso de AsyncTask.

public class TaskRunner {
    private final Executor executor = Executors.newSingleThreadExecutor(); // change according to your requirements
    private final Handler handler = new Handler(Looper.getMainLooper());

    public interface Callback<R> {
        void onComplete(R result);
    }

    public <R> void executeAsync(Callable<R> callable, Callback<R> callback) {
        executor.execute(() -> {
            final R result = callable.call();
            handler.post(() -> {
                callback.onComplete(result);
            });
        });
    }
}

¿Cómo pasar el String? Al igual que:

class LongRunningTask implements Callable<MyPojo> {
    private final String input;

    public LongRunningTask(String input) {
        this.input = input;
    }

    @Override
    public MyPojo call() {
        // Some long running task
        return myPojo;
    }
}

Y

// in ViewModel
taskRunner.executeAsync(new LongRunningTask(input), (data) -> {
    // MyActivity activity = activityReference.get();
    // activity.progressBar.setVisibility(View.GONE);
    // populateData(activity, data) ;

    loadingLiveData.setValue(false);
    dataLiveData.setValue(data);
});

// in Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main_activity);

    viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
    viewModel.loadingLiveData.observe(this, (loading) -> {
        if(loading) {
            progressBar.setVisibility(View.VISIBLE);
        } else {
            progressBar.setVisibility(View.GONE);
        }
    });

    viewModel.dataLiveData.observe(this, (data) -> {
        populateData(data);
    }); 
}

Este ejemplo usó un grupo de subproceso único que es bueno para escrituras de bases de datos (o solicitudes de red serializadas), pero si desea algo para lecturas de bases de datos o solicitudes múltiples, puede considerar la siguiente configuración de Ejecutor:

private static final Executor THREAD_POOL_EXECUTOR =
        new ThreadPoolExecutor(5, 128, 1,
                TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

Puede usar ejecutores directamente desde el java.util.concurrentpaquete.

También busqué al respecto y encontré una solución en esta publicación Android Async API is Obsoleta .

Desafortunadamente, la publicación usa Kotlin, pero después de un poco de esfuerzo, la convertí a Java. Así que aquí está la solución.

    ExecutorService executor = Executors.newSingleThreadExecutor();
    Handler handler = new Handler(Looper.getMainLooper());

    executor.execute(new Runnable() {
        @Override
        public void run() {

            //Background work here

            handler.post(new Runnable() {
                @Override
                public void run() {
                    //UI Thread work here
                }
            });
        }
    });

Bastante simple, ¿verdad? Puede simplificarlo un poco más si está utilizando Java8 en su proyecto.

    ExecutorService executor = Executors.newSingleThreadExecutor();
    Handler handler = new Handler(Looper.getMainLooper());

    executor.execute(() -> {
        //Background work here
        handler.post(() -> {
            //UI Thread work here
        });
    });

Aún así, no puede superar los términos de concisión del código de Kotlin, pero es mejor que la versión anterior de Java.

Espero que esto te ayudará. Gracias

Una de las alternativas más sencillas es utilizarThread

new Thread(new Runnable() {
    @Override
    public void run() {
        // do your stuff
        runOnUiThread(new Runnable() {
            public void run() {
                // do onPostExecute stuff
            }
        });
    }
}).start();

Si su proyecto es compatible con JAVA 8, puede usar lambda

new Thread(() -> {
        // do background stuff here
        runOnUiThread(()->{
            // OnPostExecute stuff here
        });
    }).start();

De acuerdo con la documentación de Android, AsyncTask quedó en desuso en el nivel de API 30 y se sugiere usar las utilidades de concurrencia estándar java.util.concurrent o Kotlin en su lugar.

Usando este último se puede lograr bastante simple:

  1. Crear función de extensión genérica en CoroutineScope:

     fun <R> CoroutineScope.executeAsyncTask(
             onPreExecute: () -> Unit,
             doInBackground: () -> R,
             onPostExecute: (R) -> Unit
     ) = launch {
         onPreExecute() // runs in Main Thread
         val result = withContext(Dispatchers.IO) { 
             doInBackground() // runs in background thread without blocking the Main Thread
         }
         onPostExecute(result) // runs in Main Thread
     } 
    
  2. Use la función con cualquier CoroutineScope:

    • en ViewModel:

      class MyViewModel : ViewModel() {
      
          fun someFun() {
              viewModelScope.executeAsyncTask(onPreExecute = {
                  // ... runs in Main Thread
              }, doInBackground = {
                  // ... runs in Worker(Background) Thread
                  "Result" // send data to "onPostExecute"
              }, onPostExecute = {
                  // runs in Main Thread
                  // ... here "it" is the data returned from "doInBackground"
              })
          }
      }
      
    • en Activityo Fragment:

      lifecycleScope.executeAsyncTask(onPreExecute = {
          // ... runs in Main Thread
      }, doInBackground = {
          // ... runs in Worker(Background) Thread
          "Result" // send data to "onPostExecute"
      }, onPostExecute = {
          // runs in Main Thread
          // ... here "it" is the data returned from "doInBackground"
      })
      

    Para usar viewModelScopeo lifecycleScopeagregar las siguientes líneas a las dependencias del archivo build.gradle de la aplicación:

    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$LIFECYCLE_VERSION" // for viewModelScope
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:$LIFECYCLE_VERSION" // for lifecycleScope
    

    En el momento de escribir final LIFECYCLE_VERSION = "2.3.0-alpha05"

ACTUALIZAR :

También podemos implementar la actualización del progreso usando onProgressUpdatela función:

fun <P, R> CoroutineScope.executeAsyncTask(
        onPreExecute: () -> Unit,
        doInBackground: suspend (suspend (P) -> Unit) -> R,
        onPostExecute: (R) -> Unit,
        onProgressUpdate: (P) -> Unit
) = launch {
    onPreExecute()

    val result = withContext(Dispatchers.IO) {
        doInBackground {
            withContext(Dispatchers.Main) { onProgressUpdate(it) }
        }
    }
    onPostExecute(result)
}

Usando cualquiera CoroutineScope(ver implementaciones arriba) podemos llamarlo:

someScope.executeAsyncTask(
    onPreExecute = {
        // ... runs in Main Thread
    }, doInBackground = { publishProgress: suspend (progress: Int) -> Unit ->
        
        // ... runs in Background Thread

        // simulate progress update
        publishProgress(50) // call `publishProgress` to update progress, `onProgressUpdate` will be called
        delay(1000)
        publishProgress(100)

        
        "Result" // send data to "onPostExecute"
    }, onPostExecute = {
        // runs in Main Thread
        // ... here "it" is a data returned from "doInBackground"
    }, onProgressUpdate = {
        // runs in Main Thread
        // ... here "it" contains progress
    }
)

Use esta clase para ejecutar tareas en segundo plano en Background Thread. Esta clase funciona para todas las versiones de API de Android, incluido Android 11. Además, este código es el mismo trabajo que AsyncTask con los métodos doInBackground y onPostExecute.

public abstract class BackgroundTask {

    private Activity activity;
    public BackgroundTask(Activity activity) {
        this.activity = activity;
    }

    private void startBackground() {
        new Thread(new Runnable() {
            public void run() {

                doInBackground();
                activity.runOnUiThread(new Runnable() {
                    public void run() {

                        onPostExecute();
                    }
                });
            }
        }).start();
    }
    public void execute(){
        startBackground();
    }

    public abstract void doInBackground();
    public abstract void onPostExecute();

}

Después de copiar la clase anterior, puede usarla con esto:

new BackgroundTask(MainActivity.this) {
        @Override
        public void doInBackground() {

            //put you background code
            //same like doingBackground
            //Background Thread
        }

        @Override
        public void onPostExecute() {

            //hear is result part same
            //same like post execute
            //UI Thread(update your UI widget)
        }
    }.execute();

Aquí creé una alternativa para AsyncTask usando Coroutines que se puede usar igual que AsyncTask sin cambiar mucho el código base en su proyecto.

  1. Cree una nueva clase abstracta AsyncTaskCoroutine que tome tipos de datos de parámetros de entrada y de salida, por supuesto, estos parámetros son opcionales :)

     import kotlinx.coroutines.Dispatchers
     import kotlinx.coroutines.GlobalScope
     import kotlinx.coroutines.async
     import kotlinx.coroutines.launch
    
     abstract class AsyncTaskCoroutine<I, O> {
         var result: O? = null
         //private var result: O
         open fun onPreExecute() {}
    
         open fun onPostExecute(result: O?) {}
         abstract fun doInBackground(vararg params: I): O
    
         fun <T> execute(vararg input: I) {
             GlobalScope.launch(Dispatchers.Main) {
                 onPreExecute()
                 callAsync(*input)
             }
         }
    
         private suspend fun callAsync(vararg input: I) {
             GlobalScope.async(Dispatchers.IO) {
                 result = doInBackground(*input)
             }.await()
             GlobalScope.launch(Dispatchers.Main) {
    
                 onPostExecute(result)
    
    
             }
         }
     }
    

2 . Inside Activity usa esto igual que tu antiguo AsycnTask ahora

 new AsyncTaskCoroutine() {
                @Override
                public Object doInBackground(Object[] params) {
                    return null;
                }
    
                @Override
                public void onPostExecute(@Nullable Object result) {
    
                }
    
                @Override
                public void onPreExecute() {
    
                }
            }.execute();
  1. En caso de que necesite enviar parámetros de pase

      new AsyncTaskCoroutine<Integer, Boolean>() {
    
         @Override
         public Boolean doInBackground(Integer... params) {
             return null;
         }
    
         @Override
         public void onPostExecute(@Nullable Boolean result) {
    
         }
    
         @Override
         public void onPreExecute() {
    
         }
     }.execute();
    

Para empezar, Android dejó de usar la API AsyncTask en Android 11 para deshacerse de una parte de los problemas.

Entonces, ¿qué hay ahora?

  • Hilos
  • Ejecutores
  • RxJava
  • Futuros escuchables
  • Corrutinas 🔥

¿Por qué corrutinas?

Coroutines are the Kotlin way to do asynchronous programming. Compiler support is stable since Kotlin 1.3, together with a kotlinx.coroutines library -

  • Concurrencia estructurada
  • Código secuencial sin bloqueo
  • Propagación de cancelación
  • Manejo de excepciones naturales

Google recomienda usar el marco de concurrencia de Java o Kotlin Coroutines. pero Rxjava termina teniendo mucha más flexibilidad y características que la concurrencia de Java, por lo que ganó bastante popularidad.

Simplemente reemplace toda la clase con este hilo y colóquelo en un método para pasar variables

new Thread(() -> {
            // do background stuff here
            runOnUiThread(()->{
                // OnPostExecute stuff here
              
            });
        }).start();

y en Fragmento agregue el Contexto al runOnUiThread()método:

 new Thread(() -> {
            // do background stuff here
            context.runOnUiThread(()->{
                // OnPostExecute stuff here
            });
        }).start();

De hecho, escribí dos historias de Medium al respecto:

El primero es con Java y una solución alternativa con Runnable, el segundo es una solución de Kotlin y coroutines. Ambos son con ejemplos de código, por supuesto.

Aquí también creé una alternativa para AsyncTask usando una clase abstracta y se puede copiar como una clase.

/app/src/main/java/../AsyncTasks.java

public abstract class AsyncTasks {
    private final ExecutorService executors;

    public AsyncTasks() {
        this.executors = Executors.newSingleThreadExecutor();
    }

    private void startBackground() {
        onPreExecute();
        executors.execute(new Runnable() {
            @Override
            public void run() {
                doInBackground();
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        onPostExecute();
                    }
                });
            }
        });
    }

    public void execute() {
        startBackground();
    }

    public void shutdown() {
        executors.shutdown();
    }

    public boolean isShutdown() {
        return executors.isShutdown();
    }

    public abstract void onPreExecute();

    public abstract void doInBackground();

    public abstract void onPostExecute();
}

Implementación/uso de la clase anterior

new AsyncTasks() {
     @Override
            public void onPreExecute() { 
             // before execution
      }

     @Override
            public void doInBackground() {
              // background task here 
            }

     @Override
            public void onPostExecute() { 
             // Ui task here
            }
     }.execute();

Mi reemplazo personalizado: https://github.com/JohnyDaDeveloper/AndroidAsync

Solo funciona cuando la aplicación se está ejecutando (más específicamente, la actividad que programó la tarea), pero es capaz de actualizar la interfaz de usuario después de que se completó la tarea en segundo plano.

EDITAR: Mi AsyncTask ya no requiere Activiy para funcionar.

Puede usar esta clase personalizada como una alternativa de AsyncTask<>, esto es lo mismo que AsyncTask, por lo que no necesita aplicar esfuerzos adicionales para lo mismo.

import android.os.Handler;
import android.os.Looper;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TaskRunner {

    private static final int CORE_THREADS = 3;
    private static final long KEEP_ALIVE_SECONDS = 60L;
    private static TaskRunner taskRunner = null;
    private Handler handler = new Handler(Looper.getMainLooper());
    private ThreadPoolExecutor executor;

    private TaskRunner() {
        executor = newThreadPoolExecutor();
    }

    public static TaskRunner getInstance() {
        if (taskRunner == null) {
            taskRunner = new TaskRunner();
        }
        return taskRunner;
    }

    public void shutdownService() {
        if (executor != null) {
            executor.shutdown();
        }
    }

    public void execute(Runnable command) {
        executor.execute(command);
    }

    public ExecutorService getExecutor() {
        return executor;
    }

    public <R> void executeCallable(@NonNull Callable<R> callable, @NonNull OnCompletedCallback<R> callback) {
        executor.execute(() -> {
            R result = null;
            try {
                result = callable.call();
            } catch (Exception e) {
                e.printStackTrace(); // log this exception
            } finally {
                final R finalResult = result;
                handler.post(() -> callback.onComplete(finalResult));
            }
        });
    }

    private ThreadPoolExecutor newThreadPoolExecutor() {
        return new ThreadPoolExecutor(
                CORE_THREADS,
                Integer.MAX_VALUE,
                KEEP_ALIVE_SECONDS,
                TimeUnit.SECONDS,
                new SynchronousQueue<>()
        );
    }

    public interface OnCompletedCallback<R> {
        void onComplete(@Nullable R result);
    }
}

¿Cómo usarlo? Por favor, siga los ejemplos a continuación.

Con expresiones lambda

TaskRunner.getInstance().executeCallable(() -> 1, result -> {
});


TaskRunner.getInstance().execute(() -> {
});

Sin expresiones lambda

TaskRunner.getInstance().executeCallable(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        return 1;
    }
}, new TaskRunner.OnCompletedCallback<Integer>() {
    @Override
    public void onComplete(@Nullable Integer result) {

    }
});

TaskRunner.getInstance().execute(new Runnable() {
    @Override
    public void run() {

    }
});

Nota: No olvide cerrar el servicio de ejecutores

TaskRunner.getInstance().shutdownService();

Puede migrar a los siguientes enfoques según sus necesidades

  • Hilo + controlador
  • Ejecutor
  • Futuro
  • IntentService
  • planificador de trabajos
  • RxJava
  • Corrutinas (Kotlin)

[Variantes asíncronas de Android]

La respuesta aceptada es buena. Pero... no vi la implementación del método cancel()

Entonces, mi implementación con la posibilidad de cancelar la tarea en ejecución (simulando la cancelación) se encuentra a continuación. Se necesita cancelar para no ejecutar el método postExecute() en caso de interrupción de la tarea.

public abstract class AsyncTaskExecutor<Params> {
    public static final String TAG = "AsyncTaskRunner";

    private static final Executor THREAD_POOL_EXECUTOR =
            new ThreadPoolExecutor(5, 128, 1,
                    TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private boolean mIsInterrupted = false;

    protected void onPreExecute(){}
    protected abstract Void doInBackground(Params... params);
    protected void onPostExecute(){}
    protected void onCancelled() {}

    @SafeVarargs
    public final void executeAsync(Params... params) {
        THREAD_POOL_EXECUTOR.execute(() -> {
            try {
                checkInterrupted();
                mHandler.post(this::onPreExecute);

                checkInterrupted();
                doInBackground(params);

                checkInterrupted();
                mHandler.post(this::onPostExecute);
            } catch (InterruptedException ex) {
                mHandler.post(this::onCancelled);
            } catch (Exception ex) {
                Log.e(TAG, "executeAsync: " + ex.getMessage() + "\n" + Debug.getStackTrace(ex));
            }
        });
    }

    private void checkInterrupted() throws InterruptedException {
        if (isInterrupted()){
            throw new InterruptedException();
        }
    }

    public void cancel(boolean mayInterruptIfRunning){
        setInterrupted(mayInterruptIfRunning);
    }

    public boolean isInterrupted() {
        return mIsInterrupted;
    }

    public void setInterrupted(boolean interrupted) {
        mIsInterrupted = interrupted;
    }
}

Ejemplo de uso de esta clase:

public class MySearchTask extends AsyncTaskExecutor<String> {

    public MySearchTask(){
    }

    @Override
    protected Void doInBackground(String... params) {
        // Your long running task
        return null;
    }

    @Override
    protected void onPostExecute() {
        // update UI on task completed
    }

    @Override
    protected void onCancelled() {
        // update UI on task cancelled
    }
}

MySearchTask searchTask = new MySearchTask();
searchTask.executeAsync("Test");

HandlerThread se puede usar como una alternativa de AsyncTask . Son hilos de larga duración. A continuación se muestra un ejemplo de HandlerThread:

Puede crear dos objetos de controlador. Uno de ellos se usará para enviar un mensaje de workerThread a UI Thread.

Handler uiHandler,workerHandler;
Message msg;
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
Handler.Callback callback=new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            // handle messages sent from working thread (like updating UI)...
            return true;
        }
    }
uiHandler=new Handler(callback);
workerHandler = new Handler(handlerThread.getLooper());
workerHandler.post(new Runnable(){
           // Perform required task
           uiHandler.sendMessage(msg); // this message will be sent to and handled by UI Thread
});

Además, recuerde que los HandlerThreads se ejecutan fuera del ciclo de vida de su actividad, por lo que deben limpiarse correctamente o, de lo contrario, tendrá fugas de subprocesos. Puede usar los métodos quit() o quitSafely() en onDestroy() of Activity para evitar fugas de subprocesos.

este es mi codigo

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public abstract class AsyncTaskRunner<T> {

    private ExecutorService executorService = null;
    private Set<Callable<T>> tasks = new HashSet<>();

    public AsyncTaskRunner() {
        this.executorService = Executors.newSingleThreadExecutor();
    }
    
    public AsyncTaskRunner(int threadNum) {
        this.executorService = Executors.newFixedThreadPool(threadNum);
    }


    public void addTask(Callable<T> task) {
        tasks.add(task);
    }

    public void execute() {
        try {
            List<Future<T>> features = executorService.invokeAll(tasks);

            List<T> results = new ArrayList<>();
            for (Future<T> feature : features) {
                results.add(feature.get());
            }
            this.onPostExecute(results);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
            this.onCancelled();
        } finally {
            executorService.shutdown();
        }

    }

    protected abstract void onPostExecute(List<T> results);

    protected void onCancelled() {
        // stub
    }

}

Y ejemplo de uso. Extiende la AsyncTaskRunnerclase,

class AsyncCalc extends AsyncTaskRunner<Integer> {

    public void addRequest(final Integer int1, final Integer int2) {
        this.addTask(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                // Do something in background
                return int1 + int2;
            }
        });
    }

    @Override
    protected void onPostExecute(List<Integer> results) {
        for (Integer answer: results) {
            Log.d("AsyncCalc", answer.toString());
        }
    }
}

¡entonces úsalo!

AsyncCalc calc = new AsyncCalc();
calc.addRequest(1, 2);
calc.addRequest(2, 3);
calc.addRequest(3, 4);
calc.execute();