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

Resuelto Zeeshan asked hace 54 años • 19 respuestas

Google está desaprobando la API AsyncTask de Android en Android 11 y sugiere usarla java.util.concurrenten 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 mantienes una base de código antigua con tareas asincrónicas en Android, es probable que tengas que cambiarla 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) ;
        }     


    }
Zeeshan avatar Jan 01 '70 08:01 Zeeshan
Aceptado

Puede usarlo directamente Executorsdesde java.util.concurrentel paquete.

También busqué al respecto y encontré una solución en esta publicación de API asíncrona de Android en desuso .

Desafortunadamente, la publicación usa Kotlin, pero después de un poco de esfuerzo lo convertí a Java. Entonces 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? Puedes simplificarlo un poco más si estás utilizando Java 8 en tu 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

Kashfa Khan avatar Nov 23 '2020 13:11 Kashfa Khan
private WeakReference<MyActivity> activityReference;

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

Ahora la gente tendrá la oportunidad de desinfectar su código.


AsyncTask<String, Void, MyPojo> 

Según 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 la cadena? 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 utilizó 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>());
EpicPandaForce avatar Nov 08 '2019 13:11 EpicPandaForce

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 utilizar lambda:

new Thread(() -> {
    // do background stuff here
    runOnUiThread(()->{
        // OnPostExecute stuff here
    });
}).start();
Mayank Kumar Chaudhari avatar Aug 20 '2020 07:08 Mayank Kumar Chaudhari

Según la documentación de Android AsyncTask , quedó obsoleta en el nivel de API 30 y se sugiere utilizar las utilidades de concurrencia estándar java.util.concurrent o Kotlin en su lugar.

Usando este último se puede lograr de manera bastante simple:

  1. Crear 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
     } 
    
  2. Utilice la función con cualquiera CoroutineScopeque tenga Dispatchers.Maincontexto:

    • 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 escribirfinal 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 cualquier CoroutineScope( viewModelScope/ lifecycleScope, ver implementaciones arriba) con Dispatchers.Maincontexto 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
    }
)
Sergio avatar Jul 06 '2020 03:07 Sergio