Imaginez que je suis dans un service qui a déjà un fil d'arrière-plan. Puis-je faire une demande en utilisant volley dans ce même thread, afin que les rappels se produisent de manière synchrone?
Il y a 2 raisons à cela: - Premièrement, je n'ai pas besoin d'un autre fil 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 rappel, et je n'aurai donc pas de réponse de Volley. Je sais que je peux créer mon propre service qui a un fil avec une boucle d'exécution que je peux contrôler, mais il serait souhaitable d'avoir cette fonctionnalité dans volley.
Je vous remercie!
java
android
android-volley
LocoMike
la source
la source
Réponses:
On dirait que c'est possible avec la
RequestFuture
classe de Volley . Par exemple, pour créer une requête JSON HTTP GET synchrone, vous pouvez effectuer les opérations suivantes:la source
JsonObjectRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener, ErrorListener errorlistener)
constructeur.RequestFuture<JSONObject>
implémente à la fois les interfacesListener<JSONObject>
etErrorListener
, de sorte qu'il peut être utilisé comme les deux derniers paramètres.future.get()
l'application se bloquera ou expirera à coup sûr si elle est définie.Remarque La réponse @Matthews est correcte MAIS si vous êtes sur un autre thread et que vous faites un appel de volée lorsque vous n'avez pas Internet, votre rappel d'erreur sera appelé sur le thread principal, mais le thread sur lequel vous êtes sera bloqué POUR TOUJOURS. (Par conséquent, si ce thread est un IntentService, vous ne pourrez jamais lui envoyer un autre message et votre service sera pratiquement mort).
Utilisez la version de
get()
qui a un délai d'expirationfuture.get(30, TimeUnit.SECONDS)
et détectez l'erreur pour quitter votre thread.Pour correspondre à la réponse de @Mathews:
Ci-dessous, je l'ai enveloppé dans une méthode et utilise une requête différente:
la source
IntentService
est un exécuteur de pool de threads d'un seul thread, donc IntentService sera bloqué pour toujours car il se trouve dans une boucleIl est probablement recommandé d'utiliser les Futures, mais si pour une raison quelconque vous ne le souhaitez pas, au lieu de préparer votre propre chose de blocage synchronisé, vous devriez utiliser un fichier
java.util.concurrent.CountDownLatch
. Cela fonctionnerait donc comme ça ...Étant donné que les gens semblaient essayer de faire cela et rencontraient des problèmes, j'ai décidé de fournir un échantillon fonctionnel "réel" de cette utilisation. Le voici https://github.com/timolehto/SynchronousVolleySample
Maintenant, même si la solution fonctionne, elle présente certaines limites. Plus important encore, vous ne pouvez pas l'appeler sur le thread principal de l'interface utilisateur. Volley exécute les requêtes en arrière-plan, mais par défaut, Volley utilise le principal
Looper
de l'application pour envoyer les réponses. Cela provoque un blocage car le thread d'interface utilisateur principal attend la réponse, maisLooper
attend laonCreate
fin avant de traiter la livraison. Si vous voulez vraiment faire cela, vous pouvez, au lieu des méthodes d'assistance statiques, instancier la vôtre en laRequestQueue
passant la vôtreExecutorDelivery
lié à un enHandler
utilisant unLooper
qui est lié à un thread différent du thread principal de l'interface utilisateur.la source
Comme observation complémentaire aux réponses @Blundells et @Mathews, je ne suis pas sûr qu'un appel soit livré à autre chose que le fil principal de Volley.
La source
En regardant l'
RequestQueue
implémentation, il semble que l'RequestQueue
utilisation de aNetworkDispatcher
pour exécuter la requête et de aResponseDelivery
pour fournir le résultat (leResponseDelivery
est injecté dans leNetworkDispatcher
). LeResponseDelivery
est à son tour créé avec unHandler
spawn du thread principal (quelque part autour de la ligne 112 dans leRequestQueue
implémentation).Quelque part à propos de la ligne 135 dans l'
NetworkDispatcher
implémentation, il semble que des résultats positifs soient également fournis par le biaisResponseDelivery
de toute erreur. Encore; aResponseDelivery
basé sur unHandler
spawn du thread principal.Raisonnement
Pour le cas d'utilisation où une demande doit être faite à partir d'un
IntentService
il est juste de supposer que le thread du service devrait se bloquer jusqu'à ce que nous ayons une réponse de Volley (pour garantir une portée d'exécution vivante pour gérer le résultat).Solutions suggérées
Une approche serait de remplacer la manière par défaut de
RequestQueue
créer a , où un constructeur alternatif est utilisé à la place, en injectant unResponseDelivery
qui apparaît à partir du thread actuel plutôt que du thread principal. Cependant, je n'ai pas étudié les implications de cela.la source
finish()
méthode de laRequest
classe et laRequestQueue
classe sont des packages privés, mis à part l'utilisation d'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 empêcher que quoi que ce soit ne s'exécute sur le thread principal (UI), a été de configurer un thread Looper alternatif (en utilisantLooper.prepareLooper(); Looper.loop()
) et de passer uneExecutorDelivery
instance auRequestQueue
constructeur avec un gestionnaire pour ce looper. Vous avez les frais généraux d'un autre boucleur mais restez en dehors du fil de discussion principalJ'utilise un verrou pour obtenir cet effet maintenant, je me demande si c'est correct à ma façon que quelqu'un veut commenter?
la source
catch (InterruptedException e)
intérieur de la boucle while. Sinon, le thread n'attendra pas s'il est interrompu pour une raison quelconqueJe veux ajouter quelque chose à la réponse acceptée de Matthew. Tandis que
RequestFuture
puisse sembler faire un appel synchrone à partir du thread que vous l'avez créé, ce n'est pas le cas. Au lieu de cela, l'appel est exécuté sur un thread d'arrière-plan.D'après ce que je comprends après avoir parcouru la bibliothèque, les requêtes dans le
RequestQueue
sont réparties dans sastart()
méthode:Maintenant, les deux classes
CacheDispatcher
etNetworkDispatcher
étendent thread. Ainsi, un nouveau thread de travail est généré 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 parRequestFuture
.Bien que votre deuxième objectif soit atteint, mais votre premier objectif ne l'est pas, car un nouveau thread est toujours généré, quel que soit le thread à partir duquel vous exécutez
RequestFuture
.En bref, une véritable requête synchrone n'est pas possible avec la bibliothèque Volley par défaut. Corrigez-moi si je me trompe.
la source
Vous pouvez faire une demande de synchronisation avec volley mais vous devez appeler la méthode dans un thread différent ou votre application en cours d'exécution bloquera, cela devrait être comme ceci:
après cela, vous pouvez appeler la méthode dans le thread:
la source