'Why is future.get() always timing out for Volley RequestFuture?

I am trying to make a synchronous Volley networking request. I am using request futures to wait on a response, but the future.get() call always times out (no matter how long the timeout is set to). I have tested every component individually and nothing seems to be causing the error other than my use of futures. Any ideas on what I've don wrong here?

Activity.java: persistData()

postCampaign(campaign);

Activity.java: postCampaign()

private boolean postCampaign(final Campaign c) {
    RequestFuture<String> future = RequestFuture.newFuture();
    StringRequest request = new StringRequest(Request.Method.POST, url, future, future) {
        @Override
        protected Map<String, String> getParams()
        {
            Map<String, String>  params = new HashMap<>();
            // put data
            return params;
        }
    };
    NetworkController.getInstance(this).addToRequestQueue(request);

    try {
        String response = future.get(5, TimeUnit.SECONDS);
        Log.d("Volley", "" + response);
        return !response.contains("Duplicate");
    } catch (InterruptedException|ExecutionException|TimeoutException e) {
        Log.d("Volley", "[FAILED] " + e.getClass());
        return false;
    }
}

Strangely enough though, when stepping through the code, it appears that the RequestFuture's onResponse method is invoked with the appropriate response. So it seems like the RequestFuture just isn't handling the response properly.

enter image description here



Solution 1:[1]

I think I've come to the conclusion that either Volley is not capable of fully synchronous networking requests or at the very least it isn't worth it. I ended up just showed a spinner on start and stopped it in the Volley request's onResponse method when the server responded with a success message. As far as the user is concerned it works the same way.

Solution 2:[2]

I think I've come to the conclusion that either Volley is not capable of fully synchronous networking requests or at the very least it isn't worth it.

Not exactly. You can do it but it's not in the manner that we're used to when doing synchronous network calls.

There are a couple of things you need to remember when trying to do a synchronous request in volley:

  1. The synchronous request needs to run in another thread. This is almost obvious anyway especially in recent versions of Android it will not allow you to do network calls in the main thread.
  2. Once you launch that request in another thread you cannot block on that thread to wait for it to finish or the future.get(...) call fails.

so in the above example you can simply use an executor like this:

val campaign = ...
val executor = Executors.newSingleThreadExecutor()
val f = executor.execute {  
  postCampaign(campaign)
}

in that same strategy you CANNOT wait on the future f to complete by doing this:

f.get() // this blocks thread; your volley requests will timeout and you will be sad

now, you're probably asking: How do i update UI depending on the result of future.get() if i can't wait for it to finish? it's simple: the magic of closure variable capture you can still use the result of future.get() and be able to update your UI but you do that on the main thread...with coroutines this is rather easy:

val response = future.get(5, TimeUnit.SECONDS);
Log.d("Volley", "" + response);
CoroutineScope(Dispatchers.Main).launch { 
   // you can do whatever UI updates here based on the value of result
   binding.textView.text = result
}

You can also use View::post but it has a bit more boilerplate and less elegant/flexible than coroutines (IMO).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Caleb Whittington
Solution 2 Dexter Legaspi