API Android AsyncTask obsolète dans Android 11. Quelles sont les alternatives?

21

EDIT : Cette question n'est pas un doublon comme

  1. Il y a quelques jours à peine, l'AOSP s'est engagé sur la dépréciation.
  2. L'autre question concerne l'utilisation d'AsyncTaskLoader sur AsyncTask.


Google déconseille l'API Android AsyncTask dans Android 11 et suggère de l'utiliser à la java.util.concurrentplace. vous pouvez consulter le commit ici

 *
 * @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 vous conservez une base de code plus ancienne avec des tâches asynchrones dans Android, vous devrez probablement la modifier à l'avenir. Ma question est que ce qui devrait être un remplacement correct de l'extrait de code ci-dessous en utilisant java.util.concurrent. Il s'agit d'une classe interne statique d'une activité. Je cherche quelque chose qui fonctionnera avecminSdkVersion 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
la source
5
"Obsolète" signifie que Google vous recommande de passer à autre chose. Cela ne signifie pas que la classe sera supprimée de sitôt. En particulier, AsyncTaskne peut pas être supprimé sans rompre la compatibilité descendante.
CommonsWare
2
@ Style-7 ce n'est pas le cas.
EpicPandaForce
Il n'existe pas de "classe intérieure statique". Vous voulez dire une classe imbriquée statique.
beroal

Réponses:

23
private WeakReference<MyActivity> activityReference;

Bon débarras qu'il est obsolète, car WeakReference<Context>c'était toujours un hack, et pas une bonne solution .

Désormais, les utilisateurs auront la possibilité de désinfecter leur code.


AsyncTask<String, Void, MyPojo> 

Basé sur ce code, Progressn'est en fait pas nécessaire, et il y a une Stringentrée + MyPojosortie.

C'est en fait assez facile à réaliser sans aucune utilisation d'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);
            });
        });
    }
}

Comment passer dans la chaîne? Ainsi:

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;
    }
}

Et

// 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);
    }); 
}

Cet exemple a utilisé un pool à un seul thread qui est bon pour les écritures DB (ou les requêtes réseau sérialisées), mais si vous voulez quelque chose pour les lectures DB ou les requêtes multiples, vous pouvez considérer la configuration Executor suivante:

private static final Executor THREAD_POOL_EXECUTOR =
        new ThreadPoolExecutor(5, 128, 1,
                TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
EpicPandaForce
la source
Je reçois une erreur executor.post. Impossible de résoudre la méthode
Kris B
@KrisB apparemment, il est appelé à la execute()place depost()
EpicPandaForce
Savez-vous si c'est ok de passer un Contextdans ça? Je sais que le passage d' un Contexten AsyncTaskétait l' une des questions est tout.
Kris B
Vous devez toujours l'exécuter dans un ViewModel, un fragment conservé, un singleton ou quoi que ce soit d'autre, comme vous le feriez normalement avec tout ce qui est asynchrone dans Android.
EpicPandaForce
1
newSingleThreadExecutorest préférable pour les écritures, mais vous devez absolument utiliser le THREAD_POOL_EXECUTORà la fin du message pour les lectures de la base de données.
EpicPandaForce
3

Google recommande d'utiliser le framework d'accès concurrentiel de Java ou Kotlin Coroutines. mais Rxjava finit par avoir beaucoup plus de flexibilité et de fonctionnalités que la concurrence java, ce qui lui a valu un peu de popularité.

Wahab Khan Jadon
la source