141 votes

Puis-je faire une demande synchrone avec volley ?

Imaginez que je suis dans un service qui a déjà un fil de fond. Puis-je faire une demande en utilisant volley dans ce même thread, de sorte que les rappels soient synchronisés ?

Il y a deux raisons à cela :

  • Premièrement, je n'ai pas besoin d'un autre fil de discussion et ce serait un gaspillage de le créer.
  • Deuxièmement, si je suis dans un ServiceIntent, l'exécution du thread se terminera avant le callback, et je n'aurai donc pas de réponse de Volley. Je sais que je peux créer mon propre Service qui possède un thread avec un runloop que je peux contrôler, mais il serait souhaitable d'avoir cette fonctionnalité dans Volley.

6 votes

Ne manquez pas de lire la réponse de @Blundell ainsi que la réponse très remontée (et très utile).

2voto

dbm Points 3814

En complément des réponses de @Blundells et de @Mathews, je ne suis pas certain que tout l'appel est délivré à n'importe quel endroit mais le fil principal par Volley.

La source

En regardant le RequestQueue mise en œuvre il semble que le RequestQueue utilise un NetworkDispatcher pour exécuter la demande et un ResponseDelivery pour délivrer le résultat (le ResponseDelivery est injecté dans le NetworkDispatcher ). Le site ResponseDelivery est à son tour créé avec un Handler depuis le fil principal (quelque part autour de la ligne 112 dans le fichier RequestQueue mise en œuvre).

Quelque part à la ligne 135 dans le NetworkDispatcher mise en œuvre il semble que les résultats fructueux soient également obtenus par le biais de la même ResponseDelivery comme des erreurs éventuelles. Encore une fois, un ResponseDelivery sur la base d'un Handler à partir du fil principal.

Justification

Pour le cas d'utilisation où une demande doit être faite à partir d'une IntentService il est juste de supposer que le thread du service doit bloquer jusqu'à ce que nous ayons une réponse de Volley (pour garantir une portée d'exécution vivante dans laquelle traiter le résultat).

Solutions proposées

Une approche consisterait à remplacer le mode par défaut d'une RequestQueue est créé où un constructeur alternatif est utilisé à la place, en injectant un fichier de type ResponseDelivery qui surgit de la actuel plutôt que le fil principal. Je n'ai toutefois pas étudié les implications de cette décision.

1 votes

La mise en œuvre d'une implémentation personnalisée de ResponseDelivery est compliquée par le fait que l'élément finish() dans la méthode Request et la classe RequestQueue sont privées, à part utiliser un hack de réflexion, je ne suis pas sûr qu'il y ait un moyen de contourner cela. Ce que j'ai fini par faire pour éviter que quoi que ce soit ne s'exécute sur le thread principal (UI) a été de mettre en place un thread Looper alternatif (à l'aide de Looper.prepareLooper(); Looper.loop() ) et en passant un ExecutorDelivery à l'instance RequestQueue avec un gestionnaire pour ce looper. Vous bénéficiez de l'avantage d'une autre boucle, mais vous restez en dehors du fil d'exécution principal.

2voto

Vignatus Points 43

Je voudrais ajouter quelque chose à la réponse acceptée de Matthieu. Alors que RequestFuture peut sembler effectuer un appel synchrone à partir du thread que vous avez créé, il n'en est rien. Au lieu de cela, l'appel est exécuté sur un thread d'arrière-plan.

D'après ce que j'ai compris après avoir parcouru la bibliothèque, les demandes dans le cadre de la RequestQueue sont envoyées dans son start() méthode :

    public void start() {
        ....
        mCacheDispatcher = new CacheDispatcher(...);
        mCacheDispatcher.start();
        ....
           NetworkDispatcher networkDispatcher = new NetworkDispatcher(...);
           networkDispatcher.start();
        ....
    }

Maintenant, les deux CacheDispatcher y NetworkDispatcher Les classes étendent le fil. Ainsi, un nouveau fil de travail est créé pour retirer la file d'attente des demandes et la réponse est renvoyée aux écouteurs de succès et d'erreur implémentés en interne par la classe RequestFuture .

Bien que votre deuxième objectif soit atteint, le premier ne l'est pas, car un nouveau fil est toujours créé, quel que soit le fil à partir duquel vous exécutez l'opération. RequestFuture .

En bref, La demande synchrone vraie n'est pas possible avec la bibliothèque Volley par défaut. Corrigez-moi si je me trompe.

1voto

forcewill Points 538

J'utilise une serrure pour obtenir cet effet. Je me demande maintenant si ma méthode est correcte. quelqu'un veut-il faire un commentaire ?

// as a field of the class where i wan't to do the synchronous `volley` call   
Object mLock = new Object();

// need to have the error and success listeners notifyin
final boolean[] finished = {false};
            Response.Listener<ArrayList<Integer>> responseListener = new Response.Listener<ArrayList<Integer>>() {
                @Override
                public void onResponse(ArrayList<Integer> response) {
                    synchronized (mLock) {
                        System.out.println();
                        finished[0] = true;
                        mLock.notify();

                    }

                }
            };

            Response.ErrorListener errorListener = new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    synchronized (mLock) {
                        System.out.println();
                        finished[0] = true;
                        System.out.println();
                        mLock.notify();
                    }
                }
            };

// after adding the Request to the volley queue
synchronized (mLock) {
            try {
                while(!finished[0]) {
                    mLock.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

0 votes

Je pense que vous implémentez essentiellement ce que Volley fournit déjà lorsque vous utilisez "futures".

1 votes

Je recommanderais d'avoir le catch (InterruptedException e) à l'intérieur de la boucle while. Sinon, le thread n'attendra pas s'il est interrompu pour une raison quelconque.

0 votes

@jayeffkay j'attrape déjà l'exception si une ** InterruptedException** se produit dans la boucle while le catch la gère.

0voto

Slimani Ibrahim Points 89

Vous pouvez faire une demande de synchronisation avec volley mais vous devez appeler la méthode dans un thread différent sinon votre application en cours d'exécution se bloquera, cela devrait être comme ceci :

public String syncCall(){

    String URL = "http://192.168.1.35:8092/rest";
    String response = new String();

    RequestQueue requestQueue = Volley.newRequestQueue(this.getContext());

    RequestFuture<JSONObject> future = RequestFuture.newFuture();
    JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, URL, new JSONObject(), future, future);
    requestQueue.add(request);

    try {
        response = future.get().toString();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    } catch (JSONException e) {
        e.printStackTrace();
    }

    return response;

}

après cela, vous pouvez appeler la méthode dans le fil :

 Thread thread = new Thread(new Runnable() {
                                    @Override
                                    public void run() {

                                        String response = syncCall();

                                    }
                                });
                                thread.start();

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X