AsyncTask est obsolète, maintenant quoi? – La startup
Si vous lisez ceci parce que vous venez d’entendre qu’à partir d’Android 11 AsyncTask est obsolète et que vous cherchez une sorte de solution, vous avez peut-être atteint le bon endroit.
Cet article est pour vous si vous n’avez pas déménagé vers Kotlin, vous avez besoin d’une sorte de solution Java.
Avant de commencer, je dois dire que Kotlin propose l’utilisation des étonnantes «coroutines» pour les tâches asynchrones. Si vous voulez l’essayer, vous pouvez le faire avec le lien suivant. Si vous n’avez pas commencé à travailler avec Kotlin ou au moins à lire à ce sujet, je vous suggère vraiment de le faire, car Google l’a annoncé comme langue officielle pour le développement Android (!!). Non seulement cela, mais toute leur documentation est également écrite en Kotlin pour les nouveaux composants (les anciens composants ont des exemples et de la documentation sur Kotlin ainsi que Java) et si vous n’êtes toujours pas convaincu – cela réduit les temps de développement en moyenne d’environ 40 %, ce qui signifie moins de code d’écriture, un temps de mise sur le marché plus rapide, il est intuitivement facile à lire et possède de nombreuses fonctionnalités clés que j’aime personnellement en tant que développeur. Mais cela suffit pour Kotlin, passons aux choses sérieuses.
Supposons que vous disposiez de la classe suivante qui étend AsyncTask:
public class myAsyncTask extends AsyncTask{ interface myInteface {WeakReference
myIntefaceWeakReference; public myAsyncTask(myInteface listener) {
this.myIntefaceWeakReference = new WeakReference<>(listener);
}@Override
protected void onPreExecute() {
if(myIntefaceWeakReference != null && myIntefaceWeakReference.get() != null){
myIntefaceWeakReference.get().doWorkBeforeBackground();
}
}@Override
protected String doInBackground(Void... voids) {
String result = someNetworkFunction();
return result;
}@Override
protected void onPostExecute(Object s) {
if(myIntefaceWeakReference != null && myIntefaceWeakReference.get() != null){
myIntefaceWeakReference.get().doWorkAfterBackground((String)s);
}
}
}
void doWorkBeforeBackground();
void doWorkAfterBackground(Object result);
}
Voyons maintenant la transformation que j’offre, je vais d’abord montrer le code et l’expliquer ensuite:
public interface CustomCallable extends Callable{
void setDataAfterLoading();
void setUiForLoading(R result);
}
J’ai créé une interface CustomCallable qui étend Callable, j’utilise le
public class TaskRunner {private final Handler handler = new Handler(Looper.getMainLooper());
private final Executor executor = Executors.newCachedThreadPool();
public void executeAsync(CustomCallable callable) {
try {
callable.setUiForLoading();
executor.execute(new RunnableTask(handler, callable));
} catch (Exception e) {
Utils.printStackTrace(e);
}
}
public static class RunnableTask implements Runnable{
private final Handler handler;
private final CustomCallable callable;
public RunnableTask(Handler handler, CustomCallable callable) {
this.handler = handler;
this.callable = callable;
}
@Override
public void run() {
try {
final R result = callable.call();
handler.post(new RunnableTaskForHandler(callable, result));
} catch (Exception e) {
Utils.printStackTrace(e);
}
}
}
public static class RunnableTaskForHandler implements Runnable{
private CustomCallable callable;
private R result;
public RunnableTaskForHandler(CustomCallable callable, R result) {
this.callable = callable;
this.result = result;
}
@Override
public void run() {
callable.setDataAfterLoading(result);
}
}
}
Vous pouvez voir que j’ai ajouté une classe nommée TaskRunner, cela agira comme une classe gestionnaire-wrapper. Cette classe a un gestionnaire et un exécuteur (pas comme le Pokémon) pour gérer le travail des threads avec ThreadPool. le executeAsync est la fonction qui appelle le setUiForLoading() fonction que vous n’avez pas à implémenter au cas où vous n’auriez rien à faire. Ensuite, l’exécuteur crée la RunnableTask (équivalente à AsyncTasks doInBackground). Ce runnable est un Runnable générique grâce à
le RunnableTask.run () la fonction gère la appel() et retourne ses résultats afin que nous puissions le publier sur le gestionnaire pour revenir au thread d’interface utilisateur et enfin utiliser le setDataAfterLoading (résultat) (équivalent à onPostExecute).
L’idée de base derrière cela est de créer un run générique avec 3 fonctions principales:
- setUiForLoading() sur le thread principal de l’interface utilisateur.
- setDataAfterLoading(Résultat R) sur le thread d’interface utilisateur principal.
- appel() fonction qui renvoie un résultat générique en tant qu’objet qui s’exécute sur un thread différent.
Si vous êtes allé si loin – ce n’est pas un long chemin à parcourir.
Afin de le garder comme une implémentation simple, j’ai créé cette classe de base abstraite:
public abstract class BaseTask implements CustomCallable {
@Override
public void setUiForLoading() {}
@Override
public void setDataAfterLoading(R result) {
}
@Override
public R call() throws Exception {
return null;
}
}
Créons une classe qui étend la BaseTask
public class NetworkTask extends BaseTask {private final iOnDataFetched listener;//listener in fragment that shows and hides ProgressBar
public NetworkTask(iOnDataFetched onDataFetchedListener,) {
this.listener = onDataFetchedListener;
}@Override
public Object call() throws Exception {Object result = null;
}
result = someNetworkFunction();//some network request for example
return result;@Override
listener.showProgressBar(); }
public void setUiForLoading() {@Override
listener.setDataInPageWithResult(result); listener.hideProgressBar(); }
public void setDataAfterLoading(Object result) {
}public interface iOnDataFetched{ void showProgressBar();
void hideProgressBar();
void setDataInPageWithResult(Object result);}
Maintenant, pour qu’il s’exécute, nous remplaçons le code suivant dans le fragment:
new myAsyncTask(this).execute();
Avec:
TaskRunner runner = new TaskRunner();runner.executeAsync(new NetworkTask(this));
Et ‘voua la’ la magie opère. Pour chaque tâche Async que vous souhaitez remplacer, il vous suffit de créer une fonction qui étend la BaseTask, de copier le code de votre AsyncTask précédente et de remplacer l’appel de l’ancienne AsyncTask vers le nouveau TaskRunner et d’exécuter la nouvelle classe.
En utilisant cette implémentation, vous devez construire une fois TaskRunner, BaseTask objet, myInteface. Après cela, il vous suffit de créer une nouvelle classe qui étend BaseTask et vous avez terminé. Le processus est vraiment facile à comprendre, déboguer, lire et très peu de code à réécrire pour chaque tâche que vous souhaitez créer.
J’ai dû trouver cette solution pour mon lieu de travail car nous n’avons pas encore déménagé à Kotlin. Comme je l’ai dit dans le deuxième paragraphe, c’est mieux si vous utilisez Kotlins coroutines, mais si vous n’aviez pas le choix comme moi et que vous souhaitez vous épargner un peu d’écriture pour l’avenir – profitez de ces morceaux de code.
Je ferai un autre article qui convertit la tâche AsyncTask en coroutines Kotlins alors attendez un peu.
Merci pour la lecture.