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 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 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) ;
}
}
Puede usarlo directamente Executors
desde java.util.concurrent
el 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
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, Progress
en realidad no es necesario y hay una String
entrada + MyPojo
salida.
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>());
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();
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:
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 }
Utilice la función con cualquiera
CoroutineScope
que tengaDispatchers.Main
contexto: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
oFragment
: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
olifecycleScope
agregar 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 onProgressUpdate
la 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.Main
contexto 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
}
)