My code will not cancel AsyncTask when it should on android - java

I want to cancel an AsyncTask after a timeout if the task is still running and I want to cancel it immediately. Here is my code:
HttpURLConnection connection = null;
//GetRequestTask extends AsyncTask
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);
connection.disconnect();
}
}
}, TIME_OUT);
As you can see, I am calling a HTTP request in my AsyncTask's doInBackground method. and when I want to cancel the AsyncTask I also disconnect the connection.
The problem is that when I call cancel(true) and my app logs entered cancelling, the AsynkTask won't cancel immediately and will cancel with a minimum 10 to 20 seconds delay.
what should I do to cancel the task as soon as I call cancel?
UPDATE: This is my `AsyncTask code:
private class GetRequestsTask extends AsyncTask<URL, Void, JSONObject> {
#Override
protected void onPreExecute() {
Log.d("myapp", "entered onPreExecute");
mLoadingDialog.show();
}
#Override
protected JSONObject doInBackground(URL... urls) {
Log.d("myapp", "entered doInBackground");
try {
connection = (HttpURLConnection) urls[0].openConnection();
connection.setRequestProperty("Accept", "application/json");
int response = connection.getResponseCode();
if (response == HttpURLConnection.HTTP_OK) {
StringBuilder builder = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
try {
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
}
catch (IOException e) {
e.printStackTrace();
}
finally {
reader.close();
}
return new JSONObject(builder.toString());
}
else {
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
connection.disconnect();
}
return null;
}
#Override
protected void onPostExecute(JSONObject jsonObject) {
Log.d("myapp", "entered onPostExecute");
mLoadingDialog.dismiss();
if (jsonObject == null) {
showNoInternetDialog();
}
else {
convertJSONtoArrayList(jsonObject);
mRequestArrayAdapter.notifyDataSetChanged();
mListView.smoothScrollToPosition(0);
}
}
#Override
protected void onCancelled() {
Log.d("myapp", "entered onCancelled");
mLoadingDialog.dismiss();
showNoInternetDialog();
}
The problem is that my timeout is 20 seconds, but onCancelled is called after 35, 40 seconds.

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 cancel() 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 subscription. Then you just need to call savedSubscription.unsubscribe() and that's it.
Also, please, do not use Async tasks as inner classes.

Related

Assigning string value to activity string variable from AsyncTask's onPostExecute method not working

I created a REST API which appears to work fine (I tested the GET request of interest using Postman).
I am working to make an identical request from an Android application using an AsyncTask. The hope is to assign a string value generated by the AsyncTask to a string variable in the Android activity.
I have used a Toast to view the string output of the doInBackground method, which is passed to the onPostExecute method of the AsyncTask, to make sure that the call to the API is working and it works fine.
The problem comes when I assign the string output of the AsyncTask to the string variable defined in the Activity class. After executing the AsyncTask I am using a Toast to view the value of the string variable and it is reflecting the assignment performed in the onPostExecute method.
I will now share the code that I am using in the hope that you can help me to find where I might be going wrong. After the button is clicked, I intend for the validate AsyncTask to execute and assign a value to emailValid.
In view of the code below, my question in the most direct way I can put it: "Why is Toast 2 displaying while Toast 1 is not?"
Thank you in advance for your assistance.
public class JoinActivity extends AppCompatActivity implements View.OnClickListener
{
EditText email_et;
Button join_b;
String emailValid;
#Override
protected void onCreate(Bundle savedInstance)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_join);
emailValid = "No";
join_b = (Button) findViewById(R.id.aj_join_b);
join_b.setOnClickListener(this);
}
#Override
public void onClick(View view)
{
email_et = (EditText) findViewById(R.id.et_aj_email_address);
new JoinActivity.validate().execute(email_et.getText().toString());
// Toast 1 here:
Toast.makeText(getApplicationContext(), emailValid,
Toast.LENGTH_LONG).show();
}
private class validate extends AsyncTask<String, Void, String>
{
#Override
protected String doInBackground(String... params)
{
// ... GET request produces string with either yes or no i.e. outcome
return outcome;
}
#Override
protected void onPostExecute (String s)
{
// Toast 2 here:
Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG).show();
emailValid = s;
super.onPostExecute(s);
}
}
}
As requested, the detail in the doInBackground method is pasted below. The method actually return some XML. My original question which said "a string variable which is either a "yes" or "no"" was my attempt to simplify the setup. As I mentioned before, displaying the string in a Toast from within the onPostExecute works fine showing the participantXML sent from the doInBackground:
#Override
protected String doInBackground(String... params) {
InputStream inputStream;
String emailAddress = params[0];
String outcome = null;
HttpURLConnection httpURLConnection = null;
BufferedReader bufferedReader = null;
try
{
URL url = new URL("http://10.0.2.2:8080/project_name/webresources/entity.participant/email");
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setRequestProperty("Accept", "application/xml");
httpURLConnection.addRequestProperty("Email-address", emailAddress);
httpURLConnection.connect();
inputStream = httpURLConnection.getInputStream();
StringBuffer stringBuffer = new StringBuffer();
if (inputStream == null) {
return null;
}
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuffer.append(line + "\n");
}
if (stringBuffer.length() == 0) {
return participantXML;
}
participantXML = stringBuffer.toString();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
validEmailAddress = participantXML;
return participantXML;
}
The answer by user3691697 to this question: How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class? did the trick. I created task object (myTask) when executing the task and thereafter used it to assign the result to the variable like this:
AsyncTask myTask = new JoinActivity.validate.execute(email_et.getText().toString());
emailValid = myTask.get().toString();
The Toast message confirms that the assignment worked! Thank you for the help Pulak and LeoNeo.

how to solve the bug in picture download thread?

In my code,first I access an address and I got the text file. In that, there are many picture links, such as http://dnight-math.stor.sinaapp.com/%E5%9C%B0%E7%90%861_img004.jpg. I use regular expression to find all the links to make a arraylist. Then I use downloadService to download all the pictures. When I first press a button to download ,it can run successfully. But it doesn't work if the button is pressed again and throws error. I think this bug is about thread but I don't know how to solve it.
HttpUtil.sendHttpRequest(address,
new HttpCallbackListener() {
#Override
public void onFinish(String response) {
try {
ArrayList<String> urlList = new ArrayList<>();
Pattern p = Pattern.compile("http:.*?.com/(.*?.(jpg|png))");
Matcher m = p.matcher(response);
StringBuffer buffer = new StringBuffer();
while (m.find()) {
m.appendReplacement(buffer, "<T>" + + m.group(1) + "</T>");
urlList.add(m.group());
}
m.appendTail(buffer);
response = buffer.toString();
Message m2 = Message.obtain();
m2.obj = response;
m2.what = 1;
mHandler.sendMessage(m2);
new DownloadService("/data/data/com.baodian/files",
urlList,
new DownloadStateListener() {
#Override
public void onFinish() {
}
#Override
public void onFailed() {
}
}, context).startDownload();
;
// JSONObject singleChoice=all.getjson
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void onError(Exception e) {
}
});
public class HttpUtil {
public static void sendHttpRequest(final String address,
final HttpCallbackListener listener) {
new Thread(new Runnable() {
#Override
public void run() {
HttpURLConnection connection=null;
try {
URL url=new URL(address);
connection=(HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
connection.setDoInput(true);
connection.setDoOutput(true);
InputStream in=connection.getInputStream();
BufferedReader reader=new BufferedReader(new InputStreamReader(in,"gbk"));
StringBuilder response=new StringBuilder();
String line=null;
while ((line=reader.readLine())!=null) {
response.append(line);
}
if (listener!=null) {
listener.onFinish(response.toString());
}
} catch (Exception e) {
if (listener != null) {
listener.onError(e);
}
}
}
}).start();
}
}
If you look at SimY4's answer here,
he says that the error you're getting "means the thread pool is busy and queue is full as well".
What you currently do is call onFailed when you encounter the error. What you can do is implement
a supplementary enqueing scheme. You can cache the newer urls until the thread queue has space, create and enqueue
the new threads at that point.
The following thread might prove useful : Java executors: how to be notified, without blocking, when a task completes?

Android - Return boolean value from within a Thread

Im trying to return a boolean value from a runnable method within a Thread. I need to know whether a HTTPRequest method succeeded or not. The problem is I know the request is successful but I always get false as the response.
public boolean SmsDelivery;
SmsDelivery=sendSMS(prefix, number);
if(SmsDelivery){
//Do stuff
}
//The method itself
private boolean sendSMSinThread(final String str){
final AtomicBoolean b = new AtomicBoolean(false);
Thread thread = new Thread(new Runnable(){
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(str);
#Override
public void run() {
try {
// Execute HTTP Post Request
//HttpResponse response = httpclient.execute(httppost);
httpclient.execute(httppost);
b.set(true);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
Log.e("Thread:","Unable to generate call"+e);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e("Thread:","Unable to generate call"+e);
}
}
});
thread.start();
return b.get();
}
UPDATE
Based on the advices here i managed to get the desired result, however, I dont know which method is more suitable for my needs. Can someone recommend whats the best usage in my case? Using AsyncTask or a Thread + join method.
First method is using AsyncTask in the following manner:
SmsTask smsTask = new SmsTask();
try{
smsResult = smsTask.execute(urlString).get();
}catch (InterruptedException e){
e.printStackTrace();
}catch (ExecutionException e){
e.printStackTrace();
}
//the class itself
class SmsTask extends AsyncTask<String,Void, Boolean> {
final AtomicBoolean b = new AtomicBoolean(false);
#Override
protected Boolean doInBackground(String... params) {
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(params[0]);
try {
httpclient.execute(httppost);
b.set(true);
} catch (IOException e) {
e.printStackTrace();
}
return b.get();
}
#Override
protected void onPostExecute(Boolean result) {
// result holds what you return from doInBackground
Log.i("result from async: ",""+result);
super.onPostExecute(result);
}
}
Second method, almost as I initially posted but with the 'thread.join()' method:
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
return b.get();
You should wait until task will be performed. In this case you should run this code in single thread (new Thread is useless) or use Android's AsyncTask-like class and process result in onPostExecute method.
You could use some Observer pattern or something.
Something like this:
// have a custom Runnable
public class HTTPRequestRunnable implements Runnable {
HttpClient httpclient;
HttpPost httppost;
private HTTPRequestListner listner;
public HTTPRequestRunnable(String str, HTTPRequestListner listner) {
httpclient = new DefaultHttpClient();
httppost = new HttpPost(str);
this.listner = listner;
}
#Override
public void run() {
try {
// Execute HTTP Post Request
//HttpResponse response = httpclient.execute(httppost);
httpclient.execute(httppost);
if (listner != null)
listner.onSuccess();
} catch (ClientProtocolException e) {
if (listner != null)
listner.onFail();
Log.e("Thread:", "Unable to generate call" + e);
} catch (IOException e) {
if (listner != null)
listner.onFail();
e.printStackTrace();
Log.e("Thread:", "Unable to generate call" + e);
}
}
public void setListner(HTTPRequestListner listner) {
this.listner = listner;
}
/**
* here is your observer class
*/
public interface HTTPRequestListner {
void onSuccess();
void onFail();
}
}
Then use it like this in your method:
public void sendSMSinThread(final String str){
HTTPRequestRunnable httpRequestRunnable = new HTTPRequestRunnable(str,new HTTPRequestListner() {
#Override
public void onSuccess() {
//DO your logic here on success
}
#Override
public void onFail() {
//DO your logic here on fail
}
});
Thread thread = new Thread(httpRequestRunnable);
thread.start();
}
Here you go and i hope it will help you
There are multiple ways to achieve this.
Use a callable, instead of runnable, as callable's call method can return result
Stick to your approach, but before returning the result, call thread.join()
thread.start();
thread.join();
return b.get();
Drawbacks
If there are thousands of SMS to be sent, it will create those many threads.
There is no use of thread creation here as you can the incoming thread itself to send SMS.
Use Runnable and Future.
a. For each SMS create a SendSms object,
b. It will create a maximum of 10 threads.
c. The send SMS and getSMSdelivery will be synchronous events. So for each SMS sent, you can get the delivery status if that's your requirement.
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class SendSms
{
private static ExecutorService pool = Executors.newFixedThreadPool(10);
public boolean submitSms(String message,String phNo)
{
Runnable run = new SendSMSThread(message,phNo);
Future future = pool.submit(run);
try {
if(null ==future.get())
{
return true;
}
} catch (InterruptedException | ExecutionException e) {
// SMS Sending failed.
e.printStackTrace();
return false;
}
return false;
}
private class SendSMSThread implements Runnable
{
String message;
String phNo;
public SendSMSThread(String message,String phNo)
{
this.message = message;
this.phNo = phNo;
}
public void run()
{
//Send SMS
}
}
}
All the above three solution are blocking. So it will keep the threads in BLOCKING state, thereby posing significant threat to scalability of system.
a. Use a BlockingQueue.
b. For each SMS request, add a SMSObject to BlockingQueue.
c. Use a threadpool and process the objects in Queue.
d. Once the SMS is sent successfully, save the result to another data-structure.
e. Use a threadpool, read the data from above data-structure and notify about successful SMS delivery.
Try this
thread.start();
thread.join();
return b.get();

Getting the text file's url instead of the content in java when reading txt file into a variable

I'm new in java, and im trying to read a text file from the web into a variable, but i'm getting the text file's url, instead of the content, and just can't figure out what could be the problem.
The class where i'm trying to read the file:
public class readtextfile extends AsyncTask<String, Integer, String>{
private TextView description;
public readtextfile(TextView descriptiontext){
this.description = descriptiontext;
}
#Override
protected String doInBackground(String... params) {
URL url = null;
String result ="";
try {
url = new URL("http://example.com/description1.txt");
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
String line = null;
while ((line = in.readLine()) != null) {
result+=line;
}
in.close();
}
catch (MalformedURLException e) {e.printStackTrace();}
catch (IOException e) {e.printStackTrace();}
return result;
}
protected void onProgressUpdate() {
//called when the background task makes any progress
}
protected void onPreExecute() {
//called before doInBackground() is started
}
#Override
protected void onPostExecute(String result) {
this.description.setText(result);
}
}
The Activity where i call the class:
public class PhotosActivity extends Activity {
TextView description;
String descriptiontext;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.photos_layout);
description = ((TextView)findViewById(R.id.description1));
new readtextfile(description).execute();
}
}
Try url.openConnection and the use connection object to get inputStream. The updated method would be like
protected String doInBackground(String... params) {
URL url = null;
String result = "";
try {
url = new URL("http://www.example.com/description1.txt");
URLConnection connection = url.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
result += line;
}
in.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
Update based on your comment.
You need not invoke the postExecute method. If you invoke postExecute it just execute that method. The doInBackground wont get exeuted. Instead you should use the execute method. Just like java thread.start() method invoke the overridden run() method.
When an asynchronous task is executed, the task goes through 4 steps:
onPreExecute(), invoked on the UI thread before the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.
doInBackground(Params...), invoked on the background thread immediately after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also use publishProgress(Progress...) to publish one or more units of progress. These values are published on the UI thread, in the onProgressUpdate(Progress...) step.
onProgressUpdate(Progress...), invoked on the UI thread after a call to publishProgress(Progress...). The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.
onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.
refer developer doc
Try using Scanner like this.
URL url = new URL("http://www.example.com/description1.txt");
Scanner s = new Scanner(url.openStream());

HttpURLConnection timeout settings

I want to return false if the URL takes more then 5 seconds to connect - how is this possible using Java? Here is the code I am using to check if the URL is valid
HttpURLConnection.setFollowRedirects(false);
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
con.setRequestMethod("HEAD");
return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
HttpURLConnection has a setConnectTimeout method.
Just set the timeout to 5000 milliseconds, and then catch java.net.SocketTimeoutException
Your code should look something like this:
try {
HttpURLConnection.setFollowRedirects(false);
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
con.setRequestMethod("HEAD");
con.setConnectTimeout(5000); //set timeout to 5 seconds
return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
} catch (java.net.SocketTimeoutException e) {
return false;
} catch (java.io.IOException e) {
return false;
}
You can set timeout like this,
con.setConnectTimeout(connectTimeout);
con.setReadTimeout(socketTimeout);
If the HTTP Connection doesn't timeout, You can implement the timeout checker in the background thread itself (AsyncTask, Service, etc), the following class is an example for Customize AsyncTask which timeout after certain period
public abstract class AsyncTaskWithTimer<Params, Progress, Result> extends
AsyncTask<Params, Progress, Result> {
private static final int HTTP_REQUEST_TIMEOUT = 30000;
#Override
protected Result doInBackground(Params... params) {
createTimeoutListener();
return doInBackgroundImpl(params);
}
private void createTimeoutListener() {
Thread timeout = new Thread() {
public void run() {
Looper.prepare();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (AsyncTaskWithTimer.this != null
&& AsyncTaskWithTimer.this.getStatus() != Status.FINISHED)
AsyncTaskWithTimer.this.cancel(true);
handler.removeCallbacks(this);
Looper.myLooper().quit();
}
}, HTTP_REQUEST_TIMEOUT);
Looper.loop();
}
};
timeout.start();
}
abstract protected Result doInBackgroundImpl(Params... params);
}
A Sample for this
public class AsyncTaskWithTimerSample extends AsyncTaskWithTimer<Void, Void, Void> {
#Override
protected void onCancelled(Void void) {
Log.d(TAG, "Async Task onCancelled With Result");
super.onCancelled(result);
}
#Override
protected void onCancelled() {
Log.d(TAG, "Async Task onCancelled");
super.onCancelled();
}
#Override
protected Void doInBackgroundImpl(Void... params) {
// Do background work
return null;
};
}
The System property sun.net.client.defaultConnectTimeout can be set. The value is in milliseconds. This will set a default timeout for each request-
Either by setting in JVM options-
-Dsun.net.client.defaultConnectTimeout=5000
OR in java code-
System.setProperty("sun.net.client.defaultConnectTimeout", "5000");
I could get solution for such a similar problem with addition of a simple line
HttpURLConnection hConn = (HttpURLConnection) url.openConnection();
hConn.setRequestMethod("HEAD");
My requirement was to know the response code and for that just getting the meta-information was sufficient, instead of getting the complete response body.
Default request method is GET and that was taking lot of time to return, finally throwing me SocketTimeoutException. The response was pretty fast when I set the Request Method to HEAD.

Categories