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.
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 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.
i am using AsyncTask class in my first activity its work fine
and call second and i am call another AsyncTask call object in onCreate
and call the webservice in doInBackground
then its give exception
android.os.networkmainthread.
i am not getting any idea as
i am new to android development
so please show me some sample or i show you my code
this is my code
public class panel extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.panel);
User_Balance_Load Balance_load=new User_Balance_Load();
Balance_load.execute();
Toast.makeText(getBaseContext(),Cls_Constant.username, Toast.LENGTH_LONG).show();
}
class User_Balance_Load extends AsyncTask<Void, Void, Void>
{
private final ProgressDialog dialog = new ProgressDialog(panel.this);
protected void onPreExecute() {
this.dialog.setMessage("Loding Diet type...");
this.dialog.show();
}
protected Void doInBackground(final Void... unused) {
// TODO Auto-generated method stub
panel.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
Update_balance();
}
});
return null;
}
protected void onPostExecute(Void result)
{
if (this.dialog.isShowing())
{
this.dialog.dismiss();
}
}
}
void Update_balance()
{
Vector result;
result=Cls_webservice.User_Balance(Cls_Constant.Guid);
if(result.size()>0)
{
String UserBalance=result.elementAt(2).toString();
TextView Tv_User_balacne=(TextView)findViewById(R.id.tv_point_balance);
Tv_User_balacne.setText(UserBalance);
}
}
And this is my class of webservice
public class Cls_webservice {
public static Vector User_Balance(String id)
{
Vector _vector = new Vector();
final String userid = id;
final String METHOD_NAME = "WCFDatatableRes";
final String SOAP_ACTION = "http://tempuri.org/WCFDatatableRes";
final String NAMESPACE = "http://tempuri.org/";
final String URL = "http://xxxxxxxx.com/GameRoom/ANDROIDLOTT/WebService.asmx";
String return_val="";
SoapObject newob;
try
{
Object response;
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.encodingStyle = SoapEnvelope.ENC;
SoapObject Request = new SoapObject(NAMESPACE , METHOD_NAME);
Request.addProperty("Params", userid+",3");
Request.addProperty("Do", "Balance");
envelope.dotNet = true;
envelope.setOutputSoapObject(Request);
AndroidHttpTransport httptransport ;
httptransport = new AndroidHttpTransport(URL);
httptransport.debug=true;
try
{
httptransport.call(SOAP_ACTION,envelope);
response = envelope.getResponse();
newob = (SoapObject)envelope.bodyIn;
return_val = newob.toString();
SoapObject diettype_listResult = (SoapObject) newob.getProperty("WCFDatatableRes ") ;
SoapObject diffgram = (SoapObject) diettype_listResult.getProperty("diffgram") ;
}
catch (Exception e) {
System.out.println("error:" + e);
}
}
catch (Exception e) {
}
return _vector;
}
}
The exception come in this line -> "httptransport.call(SOAP_ACTION,envelope);
so please help me
and this same code work in
my first activity
i don't know why in second come error
thanks
NetworkOnMainThread Exception occurs because you are running a network related operation on the UI Thread. This is only thrown for applications targeting the Honeycomb SDK or higher. Applications targeting earlier SDK versions are allowed to do networking on their main event loop threads, but it's heavily discouraged. See the document Designing for Responsiveness.
Your are running Update_balance() by using runonUithread.
You are attempting to update ui on in doInBackground() by calling Update_balance() which sets the text in textview as below. You should not update ui in doInBackground().
TextView Tv_User_balacne=(TextView)findViewById(R.id.tv_point_balance);
Tv_User_balacne.setText(UserBalance);TextView Tv_User_balacne=(TextView)findViewById(R.id.tv_point_balance);
Tv_User_balacne.setText(UserBalance);
All your network related operations should be done in doInBackground(). Make your webservice call in doInBackground().
You can return value from doInBackground() retrieve it in onPostExecute() and update ui accordingly.
class TheTask extends AsyncTask<Void,Void,Void>
{
protected void onPreExecute()
{ super.onPreExecute();
//display progressdialog.
}
protected void doInBackground(Void ...params)//return result here
{
//http request. do not update ui here
//call webservice
//return result here
return null;
}
protected void onPostExecute(Void result)//result of doInBackground is passed a parameter
{
super.onPostExecute(result);
//dismiss progressdialog.
//update ui using the result returned form doInbackground()
}
}
The 4 steps
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.
For more details please check the link below
http://developer.android.com/reference/android/os/AsyncTask.html
You're using an asynctask and then in your asynctask you're running code under the UI thread. That is nonsense, since the result is the same as executing the code on the OnCreate() method.
protected Void doInBackground(final Void... unused)
{
Vector result = Update_balance();
panel.this.runOnUiThread(new Runnable()
{
#Override
public void run()
{
if(result.size()>0)
{
String UserBalance=result.elementAt(2).toString();
TextView Tv_User_balacne=(TextView)findViewById(R.id.tv_point_balance);
Tv_User_balacne.setText(UserBalance);
}
}
});
return null;
}
Vector Update_balance()
{
Vector result;
result=Cls_webservice.User_Balance(Cls_Constant.Guid);
return result;
}
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.
I have generic async task class which fetches response from server . And i receive those response by using get method . Now i knw that UI thread is block when i use get method , bcoz of which my progress Dialog doesnt showup on time .
Now can someone tell me alternative to do this ?? (In every case i need to send back the response to the activity which has made the call of execute so opening new activity wouldn't help me )
Code :
AsyncTask Class
public class GetDataFromNetwork extends AsyncTask<Void,String,Object> {
protected void onPreExecute() {
super.onPreExecute();
progressDialog.show();
}
protected Object doInBackground(Void... params) {
Object result = null;
try {
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER10);
new MarshalBase64().register(envelope);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
AndroidHttpTransport androidHttpTransport = new AndroidHttpTransport(ipAddress + webService);
System.setProperty("http.keepAlive", "true");
try {
androidHttpTransport.call(nameSpace + methodName, envelope);
} catch (Exception e) {
e.printStackTrace();
publishProgress(e.getMessage());
}
androidHttpTransport.debug = true;
System.out.println("response: " + androidHttpTransport.requestDump);
result = envelope.getResponse();
if(result!=null){
System.out.println("GetDataFromNetwork.doInBackground() result expection---------"+result);
}
} catch (Exception e) {
System.out.println("GetDataFromNetwork.doInBackground()-------- Errors");
e.printStackTrace();
}
return result;
}
protected void onPostExecute(Object result) {
super.onPostExecute(result);
progressDialog.dismiss();
}
Code : Activity
GetDataFromNetwork request = new GetDataFromNetwork(
this,
ProgressDialog.STYLE_SPINNER,
getResources().getText(R.string.autenticate).toString());
response= (SoapObject)request.execute().get();
I'm doing stuff like this all the time in my apps and the easiest way I found was to create "Callback" interface and pass it as a parameter to my "AsyncTask"s. You do your "doInBackground()" processing and when it's finished you call the "Callback" instance from onPostExecute passing the "result" object as parameter.
Below is a very simplified version of it.
Example of Callback interface:
package example.app;
public interface Callback {
void run(Object result);
}
Example of AsyncTask using the Callback interface above:
public class GetDataFromNetwork extends AsyncTask<Void,String,Object> {
Callback callback;
public GetDataFromNetwork(Callback callback){
this.callback = callback;
}
protected void onPreExecute() {
super.onPreExecute();
progressDialog.show();
}
protected Object doInBackground(Void... params) {
Object result = null;
// do your stuff here
return result;
}
protected void onPostExecute(Object result) {
callback.run(result);
progressDialog.dismiss();
}
}
Example of how to use the classes above in your app:
class Example {
public onCreate(Bundle savedInstanceState){
//initialize
GetDataFromNetwork request = new GetDataFromNetwork(new Callback(){
public void run(Object result){
//do something here with the result
}});
request.execute();
}
}
Like yorkw says, The problem is that you make the UI thread wait for the response with this code:
response= (SoapObject)request.execute().get();
As the documentation for AsyncTask.get() says:
Waits if necessary for the computation to complete, and then retrieves
its result.
Since the UI thread waits for the response, it can't show a progress dialog. The solution is to move the code that handles the response to the onPostExecute() method:
protected void onPostExecute(Object result) {
super.onPostExecute(result);
// Now we have the response, dismiss the dialog and handle the response
progressDialog.dismiss();
response = (SoapObject) result;
}
This method will be invoked after you have the response. Meanwhile, the UI thread can take care of showing a progress dialog.
Why don't create another class in which you will put response and in there you will have get and set method. Then in onPostExecute you write it with set method and call a handler where you will do whatever you want...This is just an idea... :)
If you are using the AsyncTask, your UI thread won't be blocked. Are you downloading your data inside doInBackground() ? Because that is where you should be doing it.