I have an AsyncTask class in android which I use for all kinds of requests from a server - improtant to point out that my requests are not working in parallel, every request loads the data to the activity and after it loads the data -> the user can browse to another activity which sends another request.
I also have a TaskCanceler which is responsible to cancel the AsyncTask if it takes more than 8 seconds.
The problem -
For some reason, when I open the app and browse between, lets say, 5-6 activities, could be less or more,
(each Activity sends a request to the server -> waits for respond -> populate the Activity with data), at some point I go to an Activity and it starts to load the data, and then, after about 1 second, I see the OnCancelled() Error message ("no connection to server!").
This means that the OnCancelled() has been called - and the TaskCenceler should only call it after 8 seconds!.
I checked, and the server answers all the requests - but the android app just "doesn't wait" for one of the requests out of nowhere.
Why is it happening? its not specific to an Activity, each time it happens on a different one. why all of the sudden the TaskCenceler doesn't wait to full 8 seconds?
My AsyncTask:
private class executeRequest extends AsyncTask<HttpRequest, Void, Integer> {
private ResponseListener listener;
public executeRequest(ResponseListener responseListener) {
listener = responseListener;
// Listener in the Activity to respond to Error/Success
}
#Override
protected void onCancelled() {
handleCancelled();
// if more than 8 seconds passed
}
private void handleCancelled() {
if (taskCanceler != null && handler != null) {
handler.removeCallbacks(taskCanceler);
}
listener.onError("no connection to server!");
}
#Override
protected Integer doInBackground(HttpRequest... params) {
// TODO Auto-generated method stub
HttpRequest request = params[0];
int responseCode = -1;
HttpResponse response;
responseResult = "";
response = httpClient.execute((HttpUriRequest) request);
responseCode = response.getStatusLine().getStatusCode();
responseResult = EntityUtils.toString(response.getEntity(),
HTTP.UTF_8);
return responseCode;
}
#Override
protected void onPostExecute(Integer result) {
// handle response
}
}
TaskCanceller:
public class TaskCanceler implements Runnable {
public executeRequest task;
public TaskCanceler() {
}
public void setTask(executeRequest task) {
this.task = task;
}
#Override
public void run() {
if (task != null)
if (task.getStatus() == AsyncTask.Status.RUNNING) {
task.onCancelled();
}
}
}
This is how I start all of my Tasks:
// Setting up the Handler and the TaskCanceler
private Handler handler = new Handler();
private TaskCanceler taskCanceler = new TaskCanceler();
task = new executeRequest(responseListener, type);
taskCanceler.setTask(task);
handler.postDelayed(taskCanceler, 8 * 1000); // cancel after 8 seconds
task.execute(refuseJoinRide);
Any ideas?
Any help would be appreciated!
Maybe, the log that you are getting is actually not from the task that you recently started. It could be from other activity that you started earlier.
Related
I'm trying to implement a basic login screen for an android app. The flow is as follows:
1) User enters login information and hits submit
2) A LoginRequest which extends AsyncTask is created and executed.
3) The doInBackground will fire some http calls to validate the user credentials
4) The onPostExecute should be getting called to set the loginResults
5) Ui thread sees the login results and continues accordingly.
I'm been simplifying the code to get to the root issue but haven't had any luck so far. Here is the simplified code that still repros the issue.
Inside my activity:
private void tryLogin(String email, String password)
{
this.showProgress(true);
LoginHelper loginHelper = new LoginHelper();
LoginResult result = loginHelper.tryLogin(email, password);
this.showProgress(false);
}
This gets called from my submit buttons on click listener.
Inside LoginHelper:
TestClass test = new TestClass();
public LoginResult tryLogin(String mobileNumber, String password, int deviceId)
{
String loginUrl = "...";
new LoginRequest(test).execute(loginUrl);
while (test.result == null)
{
try {
Thread.sleep(1000);
}
catch (Exception e)
{
//...
}
}
return test.result;
}
This will execute the AsyncTask and wait for the result being continuing.
LoginRequest:
public class LoginRequest extends AsyncTask<String, Void, LoginResult>
TestClass test;
public LoginRequest(TestClass test)
{
this.test = test;
}
#Override
protected LoginResult doInBackground(String... params) {
LoginResult ret = null;
ret = new LoginResult(1,"test");
return ret;
}
#Override
protected void onPostExecute(LoginResult result) {
this.test.result = result;
}
}
I run this through the debugger with breakpoints inside the doInBackground and onPostExecute. The doInBackground executes correctly and returns the LoginResult value, but the onPostExecute breakpoint never gets hit, and my code will wait in the while loop in LoginHelper.
You are basically checking the whole time the variable 'result' of your LoginRequest. But that's not, how AsyncTask works.
From Docs:
AsyncTask allows you to perform asynchronous work on your user
interface. It performs the blocking operations in a worker thread and
then publishes the results on the UI thread, without requiring you to
handle threads and/or handlers yourself.
You can do your work in doInBackground() method and the publish you results in onPostExecute().
onPostExecute runs on UI Thread, to allow you change elements, show the result or whatever you want to do. Your problem is, that you are the whole time blocking the UI Thread with your checking method in tryLogin()
So how to solve it?
Remove the checking method:
public void tryLogin(String mobileNumber, String password, int deviceId)
{
// Starts AsynTasks, handle results there
String loginUrl = "...";
new LoginRequest().execute(loginUrl);
}
in AsyncTask:
public class LoginRequest extends AsyncTask<String, Void, LoginResult>
// Removed Constructor, if you need to pass some other variables, add it again
#Override
protected LoginResult doInBackground(String... params) {
// TODO: Change this to actual Http Request
LoginResult ret = null;
ret = new LoginResult(1, "test");
return ret;
}
#Override
protected void onPostExecute(LoginResult result) {
// Now the result arrived!
// TODO: Use the result
}
}
More Thoughts:
You probably want to store user credentials. If so, make sure the are safe. Link
You might want, depending on results, change some UI. Here's an example:
AsyncTask:
public class LoginRequest extends AsyncTask
private Activity activity;
// Constructor
public LoginRequest(Activity activity) {
this.activity = activity;
}
#Override
protected LoginResult doInBackground(String... params) {
// TODO: Change this to actual Http Request
LoginResult ret = null;
ret = new LoginResult(1, "test");
return ret;
}
#Override
protected void onPostExecute(LoginResult result) {
ActivityLogin acLogin = (ActivityLogin) activity;
if(result.equals("ok")) {
Button loginButton = (Button) acLogin.findViewById(R.id.login-button);
loginButton.setBackgroundColor(Color.GREEN);
//Finish LoginActivity
acLogin.finish();
}
else {
//TODO: Fail Handling
}
}
}
And the start it like this:
new LoginRequest(loginActivity).execute(loginUrl);
I didnt tested the code.
It's AsyncTask so it's calling the LoginRequest and while(test.result) at the same time. You got stuck in the while loop because test.result is not done returning yet. test.result is done in onPostExecute(), so if you move that while loop in that function it will work and onPostExecute() will get called. One way to solve this problem is to implement a callback interface. Put the while loop in the overrided callback method.
refer to my answer here: how to send ArrayList(Bitmap) from asyncTask to Fragment and use it in Arrayadapter
Try This
public class LoginRequest extends AsyncTask<String, Void, LoginResult>
{
TestClass test;
LoginResult ret = null;
public LoginRequest(TestClass test)
{
this.test = test;
}
#Override
protected Boolean doInBackground(String... params) {
ret = new LoginResult(1,"test");
return true;
}
#Override
protected void onPostExecute(Boolean success) {
if(success)
this.test.result = result;
}
}
Temporary solution : You can add this.test.result = result; in the doInbackground() method.
#Override
protected LoginResult doInBackground(String... params) {
LoginResult ret = null;
ret = new LoginResult(1, "test");
this.test.result = result;
return ret;
}
Please post full code to get proper solution.
I developed chat app using xmpp by smack client. I used a background thread for incoming msg notification and working fine. But now when I am in chat view then I don't want notification of incoming msg. so I removed chatmangerlistener. but it is not working .
I used my second method that when I'll come in chat view then my background thread will be close. but i saw that background thread is not closing or stoping. isCancelling method is giving me false.
this is code :-
public class incomingmsg extends AsyncTask<String, Void, String>
{
String msg;
protected String doInBackground(String... urls) {
connection = XMPPLogic.getInstance().getConnection();
// register listeners
ChatManager chatmanager = connection.getChatManager();
chatmangerlistnr = new ChatManagerListener()
{
#Override
public void chatCreated(final Chat chat, final boolean createdLocally) {
chat.addMessageListener(new MessageListener()
{
#Override
public void processMessage(Chat chat, Message message) {
msg = message.getBody();
System.out.println("Received message: "
+ (message != null ? message.getBody() : "NULL"));
GeneratNotification(msg);
}
});
}
};
connection.getChatManager().addChatListener(chatmangerlistnr);
// idle for 20 seconds
/* final long start = System.nanoTime();
while ((System.nanoTime() - start) / 1000000 < 20000) // do for 20 seconds
{
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
*/
System.out.println("is cancellable "+this.isCancelled());
return msg;
}
protected void onPostExecute(String r) {
// GeneratNotification(r);
}
}
I m confusion if isCancellable() method is false then how can i stop it? or how can I remove my chatlistener?
please expert help me.
Cancelling a task
A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns. To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)
Check the documentation for more.
to start you async:
incomingmsg aTask = new incomingmsg();
aTask.execute(...);
to stop you async
aTask.cancel(true);
by convention uses the name of its class starting with capital letter
public class incomingmsg extends AsyncTask<String, Void, String>...
change to:
public class Incomingmsg extends AsyncTask<String, Void, String>...
but it just a good practice
Full example:
AsyncTask<Void, Void, Void> myTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
//do this test ever step of you async task.
if(!isCancelled()){
//do something here
}
return null;
}
#Override
protected void onPostExecute(Void result) {
myTask = null;
}
};
//here u start u task:
myTask.execute(null, null, null);
if(myTask != null){
//here u stop u task:
myTask.cancel(true);
}
I have an app that makes numerous RESTful service calls. I execute the calls in a class extending Asynctask. If I have to cancel the asynctask, I also want to cancel the service call. Unfortunately, cancelling the async operation still allows doInBackground to complete and I can't call isCancelled() once the request is waiting for a response (which can take a little bit). Right now, from within my doInBackground method I'm registering to be notified from the UI thread if a cancel request is made, so I can abort the HttpResponse object. Here is a piece of sample code.
It has worked so far, but can I really count on it, or am I just getting lucky? Can you count on one thread to call a method in another thread?
public class AsyncTestActivity extends Activity {
private ArrayList<IStopRequestMonitor> monitors;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
}
public void stopActivity() {
if (monitors == null || monitors.size() < 1) return;
for (int i = 0; i < monitors.size(); i++) {
monitors.get(i).stopRequest();
}
}
public void addListener(IStopRequestMonitor listener) {
if (monitors == null) monitors = new ArrayList<IStopRequestMonitor>();
monitors.add(listener);
}
public void readWebpage(View view) {
DownloadWebPageTask task = new DownloadWebPageTask();
task.execute(new String[] { "http://www.mywebsite.com/feeds/rsstest.xml" });
}
private class DownloadWebPageTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
DefaultHttpClient client = new DefaultHttpClient();
final HttpGet httpGet = new HttpGet(urls[0]);
addListener(new IStopRequestMonitor() {
public void stopRequest() {
if (httpGet == null) return;
httpGet.abort();
cancel(true);
}
});
try {
HttpResponse execute = client.execute(httpGet);
InputStream content = execute.getEntity().getContent();
// handle inputstream
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
#Override
protected void onPostExecute(String result) {
Log.d("Result:", result);
}
}
interface IStopRequestMonitor {
public void stopRequest();
}
}
There is still a race here. If stopActivity() runs before the background thread has called addListener(), the listener will get added later and will never be called to abort the HttpGet.
If you are going to call cancel() from the UI thread (or whatever thread you create the AsyncTask on), you can:
Create a 'private HttpGet httpGet' field in your AsyncTask.
Override onPreExecute() and initialize httpGet there.
Override onCancel() and say 'if (httpGet != null) { httpGet.abort() }'
In doInBackground(), return immediately if isCancelled(), otherwise run.
Because this initializes httpGet on the UI thread, a cancel() call will either run before execute() (and therefore doInBackground will see isCancelled() return true), or it will run after httpGet exists and therefore the HttpGet will be aborted.
You don't need the listeners unless you are using that for something else.
you can define global object of asynctask class and using obj.cancle() method call on button click or whenever you need.
I'm writting an app that uses WebServices to retrieve data. Initially I had a private AsyncTask class for each activity that needed data from the WebService. But I've decided to make the code simpler by creating AsyncTask as a public class. All works fine, but my problem is when I want to access the retrieved data from the AsyncTask.
For example this is my AsyncTask class.
public class RestServiceTask extends AsyncTask<RestRequest, Integer, Integer> {
/** progress dialog to show user that the backup is processing. */
private ProgressDialog dialog;
private RestResponse response;
private Context context;
public RestServiceTask(Context context) {
this.context = context;
//...Show Dialog
}
protected Integer doInBackground(RestRequest... requests) {
int status = RestServiceCaller.RET_SUCCESS;
try {
response = new RestServiceCaller().execute(requests[0]);
} catch(Exception e) {
//TODO comprobar tipo error
status = RestServiceCaller.RET_ERR_WEBSERVICE;
e.printStackTrace();
}
return status;
}
protected void onPreExecute() {
response = null;
}
protected void onPostExecute(Integer result) {
if (dialog.isShowing()) {
dialog.dismiss();
}
switch (result) {
case RestServiceCaller.RET_ERR_NETWORK:
Toast.makeText(
context,
context.getResources().getString(
R.string.msg_error_network_unavailable),
Toast.LENGTH_LONG).show();
break;
case RestServiceCaller.RET_ERR_WEBSERVICE:
Toast.makeText(
context,
context.getResources().getString(
R.string.msg_error_webservice), Toast.LENGTH_LONG)
.show();
break;
default:
break;
}
}
public RestResponse getResponse() throws InterruptedException {
return response;
}
}
RestServiceCaller, RestRequest and RestResponse are clasess that I've created.
I'm using the task like this:
RestRequest request = new JSONRestRequest();
request.setMethod(RestRequest.GET_METHOD);
request.setURL(Global.WS_USER);
HashMap<String, Object> content = new HashMap<String, Object>() {
{
put(Global.KEY_USERNAME, username.getText().toString());
}
};
request.setContent(content);
RestServiceTask task = new RestServiceTask(context);
task.execute(request);
This code works fine and is calling the web service correctly, my problem is when I want access to the response. In the AsyncTask I've created the method getResponse but when I use it, it returns a null object because the AsyncTask is still in progress, so this code doesn't work:
//....
task.execute(request);
RestResponse r = new RestResponse();
r = task.getResponse();
r will be a null pointer because AsyncTask is still downloading data.
I've try using this code in the getResponse function, but it doesn't work:
public RestResponse getResponse() throws InterruptedException {
while (getStatus() != AsyncTask.Status.FINISHED);
return response;
}
I thought that with the while loop the thread will wait until the AsyncTask finishes, but what I achieved was an infinite loop.
So my question is, how could I wait until AsyncTask finishes so the getResponse method will return the correct result?
The best solution is use of the onPostExecute method, but because AsyncTask is used by many activities I have no clue what to do.
try creating a callback interface. The answer to this async task question Common class for AsyncTask in Android? gives a good explanation for it.
In my applicaiton, I use a shared DefaultHttpClient to perform all http requests. In one of my UI there is a ListView used to show data requested from server and a 'query more' button used to request more data from server by starting an AsycnTask to peform the requst.
Items of the ListView are clickable. When clicking one item my app will start a new Activity used to show details data of the clicked item. The details data request is performed in another AsyncTask, and there is the problem: when i click 'query more' button and then almost in the same time (maybe a little later) i click one item of the ListView, the latter request(details data) is normally responsed but the former request(more data)seems to be stuck in doInBackground of the AsyncTask and never return, any ideas?
Here is the code of 'query more' task:
private class SearchTask extends AsyncTask<Void, Void, Group<Venue>> {
private Exception mReason = null;
private boolean mMore = false;
private int mFrom;
public SearchTask(boolean moreSearch,int startFrom){
mMore = moreSearch;
mFrom = startFrom;
}
#Override
public void onPreExecute() {
if (DEBUG) Log.d(TAG, "SearchTask: onPreExecute()");
if(!mMore) {
setProgressBarIndeterminateVisibility(true);
setLoadingView();
}
}
#Override
public Group<Venue> doInBackground(Void... params) {
try {
return search(mFrom,Anywhered.QUERY_LIMIT);
} catch (Exception e) {
mReason = e;
}
return null;
}
#Override
public void onPostExecute(Group<Venue> venues) {
..... //omitted
}
public Group<Venue> search(int from,int limit) throws AnywhereException, LocationException,
IOException {
Anywhere anywhere = ((Anywhered) getApplication()).getAnywhere();
Location location = ((Anywhered) getApplication()).getLastKnownLocationOrThrow();
NearbyVenues nvs = anywhere.venues(LocationUtils
.createAnywhereLocation(location), mSearchHolder.query,from, limit);
Group<Venue> venues = nvs.getNearybys();
return venues;
}
}
venues method will eventually call DefaultHttpClient execute() method to perform request data from server.