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();
}
}
Related
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 have an AsyncTask that calls a webservice and gets a json object from it. I want to set a timeout for this task so that if anything goes wrong(such as internet connection being disconnected or etc), the AsyncTask get cancelled.
Here is my code so far:
#Override
protected void onStart() {
final GetRequestsTask requestTask = new GetRequestsTask();
requestTask.execute(createUrl());
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
Log.d("myapp", "entered handler");
if (requestTask.getStatus() == AsyncTask.Status.RUNNING) {
Log.d("myapp", "entered cancelling");
requestTask.cancel(true);
}
}
}, TIME_OUT);
}
The problem is that when the handler reaches "entered cancelling" point and sends the cancel signal to the AsyncTask, it won't cancel and call onCancelled() immediately (it will be called after 10 seconds or so if at all)
My GetRequestTask is linear, meaning that it does not have a loop inside it so I cannot check for isCancelled() manually inside the doInBackground() method.
What do you suggest me to do?
From this question:
If you're doing computations:
You have to check isCancelled() periodically.
If you're doing a HTTP request:
Save the instance of your HttpGet or HttpPost somewhere (eg. a public field).
After calling cancel, call request.abort(). This will cause IOException be thrown inside your doInBackground.
Additionally if you use some library to provide convenient requests, then I think it should have some cance() method. For example, for retrofit there is such method.
So in this case like for 'If you're doing a HTTP request' section. you need to:
Save the instance of your request (for retrofit it's Call)
After calling cancel, call 'cancel' method for saved instance of request.
And one more alternative is rxJava. With help of this library you can create Observable for your request and save reference to its subsribtion. Then you just need to call savedSubscription.unsibscribe() and that's it.
Yes, Asynctask takes time to call onCancelled. Basically it invokes onCancelled() when doInBackground returns.
In order to resolve that, you can check isCancelled() in doInBackground frequently and as soon as it returns true, throw an exception. It will get out of doInBackground and will immediately invoke onCancelled() call back method.
On my Android App, I'm implementing SignalR connection (https://github.com/erizet/SignalA) to connect to a Hub server to send requests and receive responses.
a sample of my code is as follows:
signalAConnection = new com.zsoft.SignalA.Connection(Constants.getHubUrl(), this, new LongPollingTransport())
{
#Override
public void OnError(Exception exception)
{
}
#Override
public void OnMessage(String message)
{
}
#Override
public void OnStateChanged(StateBase oldState, StateBase newState)
{
}
};
if (signalAConnection != null)
signalAConnection.Start();
There's also the sending bit
signalAConnection.Send(hubMessageJson, new SendCallback()
{
public void OnError(Exception ex)
{
}
public void OnSent(CharSequence message)
{
}
});
The sending and receiving will occur across activites, and some responses will be sent at random times regardless of the activity, also, the connection should be opened as long as the app is running (even if the app is running in the background) that's why I wish to implement the signalA connection as a background service
The question is should I implement it as:
1 - a Service (http://developer.android.com/reference/android/app/Service.html)
OR
2 - an Intent Service (http://developer.android.com/training/run-background-service/create-service.html)
Keeping in mind that I will need to send strings to the service and get response strings from the service.
I would be most grateful if someone would show me how to implement this kind of connection in code as a background service/intentservice.
Thanks for reading.
UPDATE:
Please see this demo activity made by the developer as how he implemented SignalA
https://github.com/erizet/SignalA/blob/master/Demo/src/com/zsoft/SignalADemo/DemoActivity.java
The problem is AQuery (which I know nothing about) is being used in this demo activity. Does AQuery run in the background all the time ?
The problem is, the latest update on SignalA mentions the following
I have changed the transport. LongPolling now uses basic-http-client
instead of Aquery for http communication. I've removed all
dependencies on Aquery.
Hence I'm not sure whether I should follow this demo activity or not
Update 2:
This is the thing that is confusing me most
in the IntentService, the OnHandleIntent method calls stopSelf after it finishes its tasks, when I actually want the code in the IntentService to keep running all the time
protected abstract void onHandleIntent (Intent intent)
Added in API level 3
This method is invoked on the worker thread with a request to process. Only one Intent is processed at a time, but the processing happens on a worker thread that runs independently from other application logic. So, if this code takes a long time, it will hold up other requests to the same IntentService, but it will not hold up anything else. When all requests have been handled, the IntentService stops itself, so you should not call stopSelf().
SignalA is running on the thread that creates and starts the connection, but all network access is done in the background. The remaining work on the starting thread is really lightweight, hence its perfectly ok to do it on the UI tread.
To answer your question, you need to have a thread running the signala connection. Therefore I think a Service is the best choice since SignalA need to be running all the time.
Regarding Aquery and the demo project. I removed all dependencies to Aquery in the libraries, not in the Demo. To be clear, you don't need Aquery to run SignalA.
In my case, what I wanted was a Service not an Intent Service, since I wanted something that would keep running until the app closes
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();
}
Well I am having a stranger problem here in my android app.
I made an alarm that starts a service, something like this:
public class ExecutaAcoes extends Service implements Runnable {
protected static final String URL = "http://www.mysite.com.br"
#Override
public void onCreate() {
Log.i("Teste","Service started");
new Thread(this).start();
}
#Override
public void run() {
try {
Log.i("Teste","Thread started");
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(URL);
HttpResponse response = client.execute(request);
HttpEntity entity = response.getEntity();
if (entity != null) {
...
} catch (Throwable e) {
Log.i("Teste","erro excepty: "+e.toString());
} finally {
};
So, every 30 seconds alarm manager starts the service and it accesses the webpage www.mysite.com.br. This code works fine in emulator but not on my device. When the phone sleeps (and the screen blackout) the webpage is not accessed and application returns an error in catch (Throwable e): "java.net.UnknownHostException: Host is unresolved" or return a socket error.
The strange thing is that if I press any button on the device and the screen lights up the application returns to work normally and the web site is accessed.
Does anyone know how to solve this problem?
Thanks for some help
Alexandre
This is normal. When device goes to sleep some functions are not available. AlarmManager holds a CPU wake lock, so that your onReceive() method gets executed when alarm goes off. Note that this only affects this method: if you start a service this guarantee does not extend to this service. Also, it only gives you CPU wake lock, meaning that CPU will be running, but not the screen and network.
To access the Wifi from your service you need to acquire the WifiManager.WifiLock. For 3g, screen and other functions see PowerManager.
Also, running network scan every 30s all the time will drain the battery in a matter of hours - users will not like this. Reconsider using server push (with wifi lock) or sync adapter.
You should try this http://developer.android.com/reference/android/net/wifi/WifiManager.WifiLock.html in order to keep the Wifi active until you finish the operation. When the phone sleeps, the Wifi closes. Hope this helps!