Google está desaprobando la API AsyncTask de Android en Android 11 y sugiere usar java.util.concurrent en su lugar. 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 con minSdkVersion 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) ; } }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>
Según este código, no se necesita el Progress y hay una entrada de String + salida de MyPojo .
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>());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.
Para empezar, Android dejó de usar la API AsyncTask en Android 11 para deshacerse de una parte de los problemas.
Las corrutinas son la forma de Kotlin de hacer programación asíncrona. El soporte del compilador es estable desde Kotlin 1.3, junto con una biblioteca
kotlinx.coroutines:
De acuerdo con la documentación de Android, AsyncTask quedó obsoleto 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:
Cree una 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 } 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 Activity o 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 viewModelScope o lifecycleScope agregue 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 la función onProgressUpdate :
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 cualquier 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 } )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.
Cree una nueva clase abstracta AsyncTaskCoroutine que tome tipos de datos de parámetros de entrada y parámetros 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();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();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.
Una de las alternativas más simples es usar Thread
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();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");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 clase AsyncTaskRunner ,
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();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();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();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() :
new Thread(() -> { // do background stuff here context.runOnUiThread(()->{ // OnPostExecute stuff here }); }).start();Puede usar ejecutores directamente desde el paquete java.util.concurrent .
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
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();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.
Puede migrar a los siguientes enfoques según sus necesidades
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.