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.
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.
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:
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.
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).
Related
SpringBoot v2.5.1
There is an endpoint requesting a long running process result and it is created somehow
(for simplicity it is Mono.fromCallable( ... long running ... ).
Client make a request and triggers the publisher to do the work, but after several seconds client aborts the request (i.e. connection is lost). And the process still continues to utilize resources for computation of a result to throw away.
What is a mechanism of notifying Project Reactor's event loop about unnecessary work in progress that should be cancelled?
#RestController
class EndpointSpin {
#GetMapping("/spin")
Mono<Long> spin() {
AtomicLong counter = new AtomicLong(0);
Instant stopTime = Instant.now().plus(Duration.of(1, ChronoUnit.HOURS));
return Mono.fromCallable(() -> {
while (Instant.now().isBefore(stopTime)) {
counter.incrementAndGet();
if (counter.get() % 10_000_000 == 0) {
System.out.println(counter.get());
}
// of course this does not work
if (Thread.currentThread().isInterrupted()){
break;
}
}
return counter.get();
});
}
}
fromCallable doesn't shield you from blocking computation inside the Callable, which your example demonstrates.
The primary mean of cancellation in Reactive Streams is the cancel() signal propagated from downstream via the Subscription.
Even with that, the fundamental requirement of avoiding blocking code inside reactive code still holds, because if the operators are simple enough (ie. synchronous), a blocking step could even prevent the propagation of the cancel() signal...
A way to adapt non-reactive code while still getting notified about cancellation is Mono.create: it exposes a MonoSink (via a Consumer<MonoSink>) which can be used to push elements to downstream, and at the same time it has a onCancel handler.
You would need to rewrite your code to eg. check an AtomicBoolean on each iteration of the loop, and have that AtomicBoolean flipped in the sink's onCancel handler:
Mono.create(sink -> {
AtomicBoolean isCancelled = new AtomicBoolean();
sink.onCancel(() -> isCancelled.set(true));
while (...) {
...
if (isCancelled.get()) break;
}
});
Another thing that is important to note in your example: the AtomicInteger is shared state. If you subscribe a second time to the returned Mono, both subscriptions will share the counter and increment it / check it in parallel, which is probably not good.
Creating these state variables inside the Consumer<MonoSink> of Mono.create ensures that each subscription gets its own separate state.
I will try to make this a simple as possible (which is more than I can say for the Java HTTP setups I have seen).
I have a decision tree (pseudo) within my Activity:
private void okOnClick(View v){
if(HttpService.isCredentialValid()){
//wait to do something
} else {
//wait to do something else
}
}
Then I have an HttpService:
public class HttpService {
public static boolean isCredentialValid(){
//GET `http://my_server:8080/is-valid?someParam=123`
//the endpoint will return a 200 or 500
}
}
I don't want isCredentialValid to do any actions to the UI, I just want it to tell me, true or false.
I don't want to tightly couple it to a button.setText() or any of that, I just want a simple contract response.code == 200
In nearly every language this is not that difficult. Can someone please set me straight here.
...Sorry for any sounds of hostility. This is one of the most elementary mechanics of nearly every codebase I have ever worked with. And I am only finding Asynchronous patterns that have no way to return something substantial to the method caller. Or I am finding methods that endanger the main thread with no way to catch errors (when there is no connection for instance).
So far I have tried something like below (adjusted the code for simplification). I have allowed this to operate on the main thread, because I do want it to synchronously block. However there is no way to catch on the scenario for bad internet connection or when the remote server does not respond:
public static boolean isCredentialValid(){
String url = "http://my_server:8080?param=123";
StrictMode.ThreadPolicy policy = new
StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
try {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(1, TimeUnit.SECONDS)
.writeTimeout(1, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.code() == 200;
} catch(Exception e){
//
//THIS DOES NOT GET HIT WHEN THERE
//IS A BAD CONNECTION OR REMOTE SERVER FAILS TO RESPOND
//the app just hangs then quits
//
Log.d("ERROR:", e.toString());
return false;
}
}
First, you shouldn't execute your request on the main thread. Also, on Android HTTP requests are meant to be executed asynchronously, it's an extremely bad practice and a code smell if you need to execute them synchronously as a return for a method. The proper way of doing what you are trying to achieve is through the usage of the callback pattern. Your method shouldn't return anything, but invoke a callback which should be received as one of its parameters. If you still have the extreme necessity of doing things synchronously because either you don't know how to handle asynchronous calls or your architecture doesn't allow it, how about using a CountDownLatch? Excuse my Kotlin, but basically it works like this:
val countDownLatch = CountDownLatch(1)
// Execute your request
countDownLatch.countDown()
try {
countDownLatch.await(30, TimeUnit.SECONDS) // Give it a 30 seconds timeout
// return the response code here.
} catch (ex: InterruptedException) {
// Catch the timeout exception
}
Anyway, you should probably re-think about the necessity of actually returning from that method in particular instead of using callbacks, what I proposed isn't exactly the best practice.
PS: This code below is REALLY a bad idea. Basically what you are doing is forcing Android to allow HTTP requests on the main thread, which will completely block the UI of your application.
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
I'm working an Android app that has to make server request and then perform actions when the request is completed. Here's some pseudo code to help explain the situation:
makeRequest(new SomeTask{
onDone() {
// Do actionB with queue
}
});
// Do actionA with queue. Must be execute first!!
Here's the implementation of makeRequest in pseudo code:
makeRequest(SomeTask task) {
if(canDoOptimization) { // if true, don't need to make request
// It's a bad idea to execute this immediately.
// Wish I could wait until the current thread of execution was done...
task.onDone();
return;
}
asyncTask = new AsyncTask<SomeTask, Void, Void>() {
doInBackground(SomeTask... task) {
// Make server request...
task.onDone();
}
}
asyncTask.execute(task);
}
Usually actionA happens before actionB as expected, but in cases where we can avoid a network requests, SomeTask.execute is called immediately. This causes actionB to occur before actionA, which is bad. Is there any way I can guarantee this doesn't happen?
I've run into this situation several times in javascript. In those cases, I would wrap the SomeTask.execute call with a setTimeout or setImmediate to maintain the proper async semantics.
For clarity, here's an example of the same bug in JavaScript: https://gist.github.com/xavi-/5882483
Any idea what I should do in Java/Android?
Welcome to world of synchronization. Mutex or lock objects are often used for that purpose. Is there a Mutex in Java?
your B task should wait on mutex which is to be signaled by task A upon its completion. That will ensure proper execution order where A task will finish before B.
Always put task.onDone() in the AsyncTask, even if it doesn't have to make a request.
makeRequest(SomeTask task) {
asyncTask = new AsyncTask<SomeTask, Void, Void>() {
void doInBackground(SomeTask... task) {
if(canDoOptimization) { // if true, don't need to make request
// It's a bad idea to execute this immediately.
// Wish I could wait until the current thread of was done...
task.onDone();
return;
} else {
// Make server request...
task.onDone();
}
}
}
asyncTask.execute(task);
}
Why can't you just switch the order of things?
// Do actionA with queue. Must be execute first!!
makeRequest(new SomeTask{
onDone() {
// Do actionB with queue
});
If actionA is asynchronous as well and performed on a separate AsyncTask, you can call makeRequest(...) on actionA's AsyncTasks's onPostExecute() method.
And btw, since Android Honeycomb version, AsyncTasks are ran on the same thread, meaning if you have several tasks they can block each other.
This is fixed by specifying that the AsyncTsak should run in a thread pool:
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) {
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
else {
asyncTask.execute();
}
I have been searching about this but I did not find my specific question. I understand that AskyncTask can be canceled using .cancel(true) but this only happens if I have a loop in which I can check the value isCanceled().
But my question here is.. how can I cancel an AsyncTask (that is stuck in httpclient.execute() ) when the user presses back? If the user navigates away from that Activity and goes to another I dont want to have an uncontrolled number of AsyncTask running because this may lead to memory problems, the user could navigate back and forth and create undetermined number of tasks. That is why I want to close them. Anyone knows a way? I post the code I use to connect:
public class Test extends Activity {
#Override
protected void onStart() {
super.onStart();
new ConnectionTask().execute("https://www.mywebserver.com/webservice.php?param1=test");
}
private class ConnectionTask extends AsyncTask<String, Void, String>{
#Override
protected String doInBackground(String... params) {
try {
HttpClient httpclient = DefaultHttpClient(params,clientConnectionManager);
HttpPost httpPost = new HttpPost(params[0]);
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
if(httpEntity != null)
return EntityUtils.toString(httpEntity);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
}
Do you know what I should add in onStop() to cancel the ongoing httpClient.execute() function? Sometimes gets stuck almost forever.
I would really appreciate your help, thank you very much in advance.
UPDATE
If I close the ConnectionManager I have to do the handshaking for https again, right? Look at this code when I create the httpClient, I use this for https:
HttpClient httpclient = DefaultHttpClient(params,clientConnectionManager);
Thank you all for your quick responses and the variety of solutions exposed here. I will try to use the timeouts (for not having to wait too much) + the cancel() function to avoid processing onPostExecute. I will tell if the results are as expected! Thank you very much you all!
according to HttpClient docs, use HttpUriRequest#abort() to abort request
1.4. Aborting requests
In some situations HTTP request execution fails to complete within the expected time frame due to high load on the target server or too many concurrent requests issued on the client side. In such cases it may be necessary to terminate the request prematurely and unblock the execution thread blocked in a I/O operation. HTTP requests being executed by HttpClient can be aborted at any stage of execution by invoking HttpUriRequest#abort() method. This method is thread-safe and can be called from any thread. When an HTTP request is aborted its execution thread - even if currently blocked in an I/O operation - is guaranteed to unblock by throwing a InterruptedIOException
There is a cancel() method in AsyncTask class. Maintain a member to asynctask and cancel it in onDestroy(). then set the member to null.
Update
Use ClientConnectionManager to shutdown the connection.
http://developer.android.com/reference/org/apache/http/conn/ClientConnectionManager.html
Update 2
Check this link to set the timeout for your connection.
How to set HttpResponse timeout for Android in Java
In onPause() or onBackButtonPressed(), call cancel() on your task. In doInBackground() right after
HttpResponse httpResponse = httpClient.execute(httpPost);
Check isCanceled() and immediatelly return if true.
Of course, you still have a risk of having multiple tasks running, but since this operation is UI driven (that is task started by user interactions) there should be at most couple of them running at the same time, provided that timeout on HttpClient is reasonable.
Update
You can shutdown connection manager as well once you determine that the task needs to be canceled. see docs
That should close sockets and cause immediate return from execute(). Connection manager is set when you create DefaultHttpClient.
My understanding is that httpClient.execute() is blocking, so there is no code running to check the value of isCancelled(). And you'd prefer not to close the Connection Manager.
This might be a bit hacky, but in lieu of any better solutions, what happens if you call Thread.interrupt() on the Thread whilst httpClient.execute() is blocking?
A quick test can verify this, just add private instance variable of type Thread inside your ConnectionTask definition, set it to Thread.currentThread() at the top of doBackground(), and add a public method that calls .interrupt() on it.
If you're lucky that will cause httpClient.execute() to immediately exit, throwing an Exception. You can catch that and do any tidup you need to before the method call ends and the AsyncTask ends naturally.
You can override
onPause() method of the activity and write the stop connection code there. If the option is specific to back button then you can override
onBackButtonPressed() method to stop the execute.
Well there is a quick solution for this problem which I used to solve in my case. It may not be the correct way, as when you press back the app will respond immediately but in background network operation will continue (until timeout if you set) without blocking the application:-
Do all you network operation in a new Service say NSERV. Make the NSERV extend the Thread class and do all you network operation in the run method. For more clarity in your code better make the activity/service starting the NSERV also extend Thread class and start the NSERV from their run method.
Then use static fields or singleton to access the variables in the activity/service from NSERV.
EX:
public class NSERV extends Service implements Runnable{
public int onStartCommand (Intent intent, int flags, int startId)
{
Thread t = new Thread (this, "abc");
t.start();
return super.onStartCommand(intent, flags, startId);
}
public void run()
{
//NETWORK OPERATION...
onDestroy();
}
public void onDestroy()
{
stopSelf();
super.onDestroy();
}
}
My client wants my servlet to be able to return results within X seconds if there is something to return, else return zt X seconds with a message like "sorry could not return within specified time"
This is really a synchronos call to a servlet with a timeout. Is there an established pattern for this sort of behavior ?
What would be the ideal way to do this ?
The request handler thread creates a BlockingQueue called myQueue and gives it to a worker thread which will place its result in the queue when it is finished. Then the handler thread calls "myQueue.poll(X, TimeUnit.SECONDS)" and returns the serialized result if it gets one or your "timeout" error if it gets null instead (meaning the "poll" call timed out). Here is an example of how it might look:
// Servlet handler method.
BlockingQueue<MyResponse> queue = new ArrayBlockingQueue<MyResponse>();
Thread worker = new Thread(new MyResponseGenerator(queue));
worker.start();
MyResponse response = queue.poll(10, TimeUnit.SECONDS);
if (response == null) {
worker.interrupt();
// Send "timeout" message.
} else {
// Send serialized response.
}
Note that thread management in general (but especially in a servlet container) is full of pitfalls, so you should become very familiar with the servlet specification and behavior of your particular servlet container before using this pattern in a production system.
Using a ThreadPool is another option to consider but will add another layer of complexity.