According to the post below:
http://techtej.blogspot.com.es/2011/03/android-thread-constructspart-4.html. It says that:
In such cases, where your application is not shutdown, but any foreground tasks have been closed or changed, all the background tasks need to know of this and try to exit gracefully
To achieve this, I am calling the cancel function on all the AsyncTask instances. Is this the right way? Also sometimes in image lazy loading, I don't keep track of all the AsyncTasks alive (and fetching images), so how to say the Android OS to cancel them too?
You can cancel AsyncTask by checking thread(AsyncTask) object's status.
private Example ex = new Example();
class Example AsyncTask<Object, Void, Void> {
#Override
protected Void doInBackground(Object... params) {
String result = null;
if(!isCancelled())
result = getHttpRestManager().send();
if(!isCancelled()) {
// some codes
}
...
return null;
}
}
public boolean cancel() {
switch(ex.getStatus()) {
case RUNNING:
case PENDING:
return ex.cancel(true);
case FINISHED:
return false;
}
return false;
}
After you cancel a thread, it's status always returns RUNNING or FINISHED. If status is not PENDING, you cannot execute thread. So you have to initialize new thread object like ex = new Example() before every .execute().
if you don't want to go back in another activity then you can use
System.exit(0);
But if there are other activities in stack,then you have to check
// AsyncTask
private Example ex = new Example();
in onDestroy method you can check it if it is running
if(ex.getStatus() == AsyncTask.Status.PENDING){
}
if(ex.getStatus() == AsyncTask.Status.RUNNING){
}
if(ex.getStatus() == AsyncTask.Status.FINISHED){
}
Related
I need to call task() function from doInBackground() of AsyncTask class. The task function contains 2 sub-async task. So the task() return immediately from doInBackground().
Is it possible to stop(or mark this task done) this task from anywhere else?
How to wrap two sub async task in one.
You don't need to call another AsyncTask from within doInBackground. As a matter of fact, once you get to a high enough API, you'll get an exception. You can call another long running method from within AsyncTask without needing to worry about a thread; you're already in a background thread. If you really feed the need, call another service, but there is no reason to do this.
To stop an AsyncTask, just override OnCancelled. Then you can just call:
task.cancel(true).
EDIT:
If you want to wait for another process to finish before you move on, you can wait for that process to finish by setting a global variable in your class or application and then doing a Thread sleep until done. You will not get an ANR because you are already in a background thread and not on the Main UI:
private boolean processIsDone = false.
//then in your method you are calling from AsyncTask:
private void myLongRunningMethod() {
//do your work here....
//at the end set
processIsDone = true;
}
//in your AsyncTask:
protected Void doInBackground(Void... params) {
//do first part of AsyncTask here
myLongRunningMethod();
do {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (!processIsDone);
//finish the process here.
return null;
}
I do not understand the question exactly but maybe this can help. Use this class in your Activity like this:
myTask = new BackgroundAsyncTask().execute();
And cancel this way:
myTask.cancel(true);
This is the code of the class:
private class BackgroundAsyncTask extends AsyncTask<Object , Object ,String> {
protected void onPreExecute(){
// Do Before execute the main task
}
protected String doInBackground(Object... param) {
//Execute the main task and return for example and String
return res;
}
protected void onPostExecute(String result) {
// You can use the String returned by the method doInBackground and process it
}
}
Hope it's help
About your first question, you can catch event when your task is cancelled by onCancelled() method. try like this:
private CancelTask extends AsyncTask {
private boolean cancelled = false;
protected void onCancelled() {
cancelled = true;
}
protected Object doInBackground(Object... obj) {
do {
// do something...
}while(!cancelled)
}
}
and call AsyncTask.cancel(true); when you want to stop.
CancelTask task = new CancelTask();
task.execute();
...
task.cancel(true);
And about second question, I want to know how you handle two "Sub-AsyncTask"s.
I will try to find a solution after you update your code.
In my Main Activity Having two different AsyncTask Running in background.
after AsyncTask its start the new Activity.
this Working fine
Main Activity---->>>>New Activity
Problem:
but my Problem is Without Completing the two AsyncTasks, it Moving to New Activity
Both Task must be complete then only go to New Activity.
Help me how to Solve this.
AsyncCallWSfor1 task1 = new AsyncCallWSfor1();
// Call execute
task1.execute();
AsyncCallWSfor2 task2 = new AsyncCallWSfor2();
// Call execute
task2.execute();
Toast.makeText(sign_in.this, "Login Successfull",Toast.LENGTH_LONG).show();
Intent intent = new Intent(MainActivivty.this,NewActvity.class);
startActivity(intent);
finish();
It will definately solve your problem do something like this
//create variable for your activity or fragment
Boolean isTask1Completed=false,isTask2Completed=false;
//on post execute of task 1
isTask1Completed = true;
if(isTask1Completed&&isTask2Completed){
//start activity here
}
//on post execute of task 2
isTask2Completed = true;
if(isTask1Completed&&isTask2Completed){
//start activity here
}
Use some simple checkers
boolean first = false;
boolean second = false;
and
AsyncTask1
onPostExecute() {
first = true;
if (first && second) startActivity();
}
AsyncTask2
onPostExecute() {
second = true;
if (first && second) startActivity();
}
Why can't you try both method using one Asynchronous Task
private class MyTask extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... url) {
String result="";
if(url.equals("m1"))
{
<!--Method one-->
result="first";
}else
{
<!--Method Two-->
result="second";
}
return result;
}
#Override
protected void onPostExecute(String result) {
if(result.equals("second")
{
<!--Start activity-->
}else
{
<!--Call Asynchronous task with second method-->
}
}
}
You need to add below code
Toast.makeText(sign_in.this, "Login Successfull",Toast.LENGTH_LONG).show();
Intent intent = new Intent(MainActivivty.this,NewActvity.class);
startActivity(intent);
finish()
in your..
AsyncCallWSfor2 task2 = new AsyncCallWSfor2();
post method it will run fine....
I would suggest working with Callbacks.
You start your (i think) API Client or something and run a query.
When it starts, it will run the request in a new Thread and once it has finished loading, it will call back to the activity which has startet the request.
On this way you can be sure, tasks will be done one after another.
Activity -> new ApiClient() -> execute(url, parameters) -> new Thread() -> run Query -> Call ApiClientCallback -> Activity receives response -> do whatever is necessary
Have a look here: http://www.techotopia.com/index.php/A_Basic_Overview_of_Android_Threads_and_Thread_handlers
Asynctasks are executed serially. You should put it on postExecute of the second task.
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.
Take a boolean variable
boolean firstCompleted=false;
In onPostExecute(-) of AsyncCallWSfor1 make it as true
firstCompleted=true;
And in onPostExecute(-) of AsyncCallWSfor2 start new Activity
if(firstCompleted){
Toast.makeText(sign_in.this, "Login Successfull",Toast.LENGTH_LONG).show();
Intent intent = new Intent(MainActivivty.this,NewActvity.class);
startActivity(intent);
}
Hope this will helps you.
I am calling another class's method from doInBackground of async task.
Now i need to stop the download when cancel is called. I am not sure where to check the value of isCancelled().
class myasync extends Asynctask{
protected String doInBackground(String... sURL) {
abc = new abc();
abc.getURLResult(sURL[0])
}
}
class abc()
{
getURLResult(String URL)
{
for(int i=0; i<fp.size(); i++){
//some text to download
}
}
}
class myclass
{
myclass()
{
myasync = new myasync();
myasync.execute("http:\\");
}
stopDownload()
{
myasync.cancel(true);
}
}
EDIT:
Have used the below solution by combining the two answers below:
1. myclass.cancel1(true);
class myclass
{
myclass()
{
myasync = new myasync();
myasync.execute("http:\\");
}
stopDownload()
{
myasync.cancel1(true);
}
}
2.
class myasync extends Asynctask{
protected String doInBackground(String... sURL) {
abc = new abc();
abc.getURLResult(sURL[0])
}
cancel1()
{
abc.cancel();
}
}
3.
class abc()
{
private boolean cancel = false;
getURLResult(String URL)
{
for(int i=0; i<fp.size(); i++){
//some text to download
if(cancel)
break;
}
}
cancel()
{
cancel = true;
}
}
The above method is working. However the methods myclass.stopDownload() is running in UI thread , and hence myasync.cancel1() and abc.cancel() are running the UI thread. And myAsync.doInBackground() and hence abc.getURLResult() are running in seperate thread. I dont know much about inter process communication. I hope this is right thing to do.
Not very nice, but you can do something like this by adding a static variable isDownloading:
protected String doInBackground(String... sURL) {
abc = new abc();
abc.getURLResult(sURL[0])
}
}
class abc()
{
getURLResult(String URL)
{
for(int i=0; i<fp.size(); i++){
if(!myclass.isDownloading){ //ADDED
break; // or Return or handle Cancel
}
//some text to dopwnload
}
}
class myclass
{
public static boolean isDownloading; // ADDED
myclass()
{
myasync = new myasync();
isDownloading = true; // ADDED
myasync.execute("http:\\");
}
stopDownload()
{
isDownloading = false; // ADDED
myclass.cancel(true);
}
}
Update:
From the AsyncTask Cancel doc. we have to check if the async task got cancelled as you say.
Calling this method will result in onCancelled(Object) being invoked
on the UI thread after doInBackground(Object[]) returns. Calling this
method guarantees that onPostExecute(Object) is never invoked. After
invoking this method, you should check the value returned by
isCancelled() periodically from doInBackground(Object[]) to finish the
task as early as possible.
To do that send the asyncTask itself to the getURLResult as parameter along with the URL:
protected String doInBackground(String... sURL) {
new abc().getURLResult("http://...", this); // this here is the asyncTask itself.
}
getURLResult(String URL, myasync myAsyncTask)
{
for(int i=0; i<fp.size(); i++){
if(myAsyncTask.isCancelled()){
break;
}
}
}
Don't use a boolean as other suggested. it's not safe at all since another AsyncTask could be started. and it is a background threads. you can't guarantee which will check the boolean first. could cancel all AsyncTasks.
Old post:
The only place you need to check for cancellation to guarantee the cancellation! is on the onPostExecute. You can't guarantee that the async task got cancelled on calling cancel method. Therefore, you need to check whether the client application asked to cancel it and the returned data is not wanted anymore.
private boolean askedForCancellation = true;
#Override
protected void onPostExecute(Object response) {
if (!askedForCancellation)
// parse response
else
// ignore. or send message to activity to stop loading if you didn't already did that when called cancel method.
}
To achieve that add the following cancel method to the AsyncTask:
public final boolean cancel(boolean mayInterruptIfRunning) {
askedForCancellation = true;
return mFuture.cancel(mayInterruptIfRunning);
}
In your class:
myasync.cancel(true);
myasync = null;
Set myasync to null is ok. because, you can't use it anymore for execution again. you will get a runtime error. you need to re-initialise it.
To check if AsyncTask asked for cancellation. check if the value of
myasync is equal to null. remember the AsyncTask asked to get
cancelled and not cancelled because there is no guarantee that it is
going to be cancelled on calling cancel. What you do is to ignore the
response on onPostExecute
I used this approach in more than 15 applications till now. No bugs and no unexpected behaviours.
I created an async task to call my server to get data from DB.
I need to process the result returned from http server call.
From my activity i calling the async task in many places. so i cant use member variable to access the result. is there any way to do?
public Result CallServer(String params)
{
try
{
new MainAynscTask().execute(params);
}
catch(Exception ex)
{
ex.printStackTrace();
}
return aResultM;//Need to get back the result
}
private class MainAynscTask extends AsyncTask<String, Void, Result> {
#Override
protected Result doInBackground(String... ParamsP) {
//calling server codes
return aResultL;
}
#Override
protected void onPostExecute(Result result) {
super.onPostExecute(result);
//how i will pass this result where i called this task?
}
Try to call the get() method of AsyncTask after you call the execute() method. This works for me
http://developer.android.com/reference/android/os/AsyncTask.html#get()
public Result CallServer(String params)
{
try
{
MainAynscTask task = new MainAynscTask();
task.execute(params);
Result aResultM = task.get(); //Add this
}
catch(Exception ex)
{
ex.printStackTrace();
}
return aResultM;//Need to get back the result
}
...
...
There are two ways i can suggest -
onPostExecute(Result) in AsyncTask. See http://developer.android.com/reference/android/os/AsyncTask.html#onPostExecute(Result)
Send a broadcast with the result as an extra.
AsyncTask is an asynchronous task so it does NOT make sense to return the result to the caller. Rather handle the result in onPostExecute() like setting the value to TextView etc. Or send a broadcast so that some other listener can handle the result.
Here's how I got around this:
1) Create an interface class that defines a signature for a method to execute on completion:
public interface AsyncIfc {
public void onComplete();
}
2) Set a property on your AsyncTask class to hold the delegate method:
public AsyncIfc completionCode;
3) Trigger the delegate from onPostExecute() in the AsyncTask:
completionCode.onComplete();
4) From your calling logic, set the delegate property to an anonymous method:
task.completionCode = new AsyncIfc() {
#Override
public void onComplete() {
// Any logic you want to happen after execution
}
};
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.
Use a handler
In your activity
Handler mHandler = new Handler() {
#Override public void handleMessage(Message msg) {
String s=(String)msg.obj;
tv.setText(s);
}
};
//result is soap object in this case.
protected void onPostExecute(SoapObject result) {
pd.dismiss();
if(result != null) {
Message msg=new Message();
msg.obj=result.getProperty(0).toString();
mHandler.sendMessage(msg);
}
I have some problem with Android AsyncTask. There is an Activity which contains some TextView a button and a picture. When an user entered this activity I start an asynctask to check whether the user can go toward from the activity (until the task not finish the button not active). Then I want to start another asyntask to get the picture.
So I made an inner class:
AsyncTask<String, Void, JSONObject>() authTask = new AsyncTask<String, Void, JSONObject>() {
#Override
protected JSONObject doInBackground(String... params) {
//call the rest api
}
#Override
protected void onPostExecute(JSONObject result) {
// check the result
// and make another asynctask
AsyncTask<String, Void, Bitmap> imageTask = new Async.... {
// get image
}
imageTask.execute();
}
}
and I call
authTask.execute(); from the UI thread.
I have a bad feeling about this, especially it seems doesn't work (it's ok few times but suddenly it "freeze": no exception just hanging and the progress bar is spinning. Nothing happens and the button won't be active.)
There is another way to get an information and when it's finished immediately start another task?
UDPATE:
I working with api level 10. In authTask I get some information which is needed to start imageTask (some id) so I have to call these tasks in a row. In api level 10 it's is possible?
Thanks in advance!
Br, Peter
you can use getStatus() checks whether the the AsyncTask is pending, running, or finished.and when finsh start your new task.like:
if(authTask .getStatus() == AsyncTask.Status.PENDING){
// My AsyncTask has not started yet
}
if(authTask .getStatus() == AsyncTask.Status.RUNNING){
// My AsyncTask is currently doing work in doInBackground()
}
if(authTask .getStatus() == AsyncTask.Status.FINISHED){
// START NEW TASK HERE
}
example for your app:
btn.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
if (authTask != null && authTask.getStatus() == AsyncTask.Status.FINISHED) {
//START YOUR NEW TASK HERE
}
else
{
//IGNORE BUTTON CLICK
}
}
});
1:
You could write the code for authTask and then for imageTask, one after the other, within a single doInBackground(). This single AsyncTask instance would be fire by a single execute() statement. This may or may not be practical depending on needed UI interactions.
2:
Edit: as noted by kabuku this information is mostly for HoneyComb+. Pre HoneyComb I would definitely go with option 1 above. executeOnExecutor() is api level 11+
In receent versions, execute() will send your AsyncTasks in series by default (ICS+). If you want to make sure this happens, specify the serial executor.
In your case this would be:
authTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
// Image task will only be done AFTER textViewTask is done
imageTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
And for newer versions a simple
...
// ICS+ and pre honeycomb (I think)
authTask.execute();
// Image task will only be done AFTER textViewTask is done
imageTask.execute();
...
From the AsycnTask.execute() documentation:
Note: this function schedules the task on a queue for a single
background thread or pool of threads depending on the platform
version. When first introduced, AsyncTasks were executed serially on a
single background thread. Starting with DONUT, this was changed to a
pool of threads allowing multiple tasks to operate in parallel. After
HONEYCOMB, it is planned to change this back to a single thread to
avoid common application errors caused by parallel execution.
PS:
To run tasks independent of each other you must use the AsyncTask.THREAD_POOL_EXECUTOR. That requires a different executor:
// Go parallel! (NOT what you want)
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
Its not a good design to nest AsyncTask. Do all the heavy lifting in doInBackground and simply post/update the results. In other words, combine the processing of second AsyncTask in your first one.
From the code that you showed it does not seem to make sense to spawn second task. Just get you image inside doInBackground of the first task right after authorization.
If you need to update UI in between, you can do it in progress update.
int count;
private void attemptConnect()
{
count = 0;
str_lang = "English";
str_wait = "Plaese Wait";
new AllQuestion().execute();
}
private class AllQuestion extends AsyncTask<String, String, String> {
ProgressDialog pg;
#Override
protected void onPreExecute() {
super.onPreExecute();
pg = new ProgressDialog(LanguageActivity.this);
pg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
pg.setMessage(str_wait);
pg.setCancelable(false);
pg.show();
}
#Override
protected String doInBackground(String... strings) {
try {
SoapObject soapObject = new SoapObject(AppConstant.NAMESPACE, AppConstant.QUESTION_SOAP_METHOD);
soapObject.addProperty("language", str_lang);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(soapObject);
HttpTransportSE se = new HttpTransportSE(AppConstant.webUrl);
se.call(AppConstant.QUESTION_SOAP_ACTION, envelope);
Object responce = envelope.getResponse();
Log.d("Question List:=>>", "" + responce);
return responce.toString();
} catch (Exception e) {
e.printStackTrace();
pg.dismiss();
return null;
}
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if (pg.isShowing()) {
pg.dismiss();
Log.i(TAG, s);
if (s != null || !s.equalsIgnoreCase("")) {
try {
JSONArray array = new JSONArray(s);
for (int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
String queId = obj.getString(TAG_QID);
String que = obj.getString(TAG_QUE);
String str_Opt = obj.getString(TAG_OPT);
question = new Question(queId, que, str_lang, str_catId, str_Opt, manager.getDateTime());
helper.insertQuestion(question);
}
count++;
if (count < 5) {
if (count == 1) {
str_lang = "German";
str_wait = "bitte warte einen Moment";
new AllQuestion().execute();
}
if (count == 2) {
str_lang = "Italian";
str_wait = "per favore aspetta un momento";
new AllQuestion().execute();
}
if (count == 3) {
str_lang = "Chinese";
str_wait = "请稍候";
new AllQuestion().execute();
}
if (count == 4) {
str_lang = "French";
str_wait = "patientez s'il-vous-plait";
new AllQuestion().execute();
}
Log.d("All Question:-", question.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
}
I have an idea to make async series in just one async task:
protected Boolean doInBackground(String... params) {
if(params[0] == "taskA") {
//do somthing
params[0] = "taskB";
}
if(params[0] == "taskB") {
//do somthing
params[0] = "taskC";
}
if(params[0] == "taskC") {
//do somthing
params[0] = "taskD";
}
if(params[0] == "taskD") {
//do somthing
return true;
}
And in your main thread just call async task like this:
ShowMyProgress(); //if you like
new MyAsyncTask().execute("taskA");
And finally you can hide your progress on onPostExecute like:
protected void onPostExecute(final Boolean success) {
if (success) {
....
HideMyProgress();
}
}
I have solved this kind of problem when i had to download something from a database before login in the user into the app, with this i fixed this problem.
To use ObservableInteger you can do this
first declare it
private ObservableInteger mObsInt;
then in your onCreate you will have a listener waiting for the values of the mObsInt to change, after those values change you can do anything you want
//Listener
mObsInt = new ObservableInteger();
mObsInt.set(0);
mObsInt.setOnIntegerChangeListener(new OnIntegerChangeListener()
{
#Override
public void onIntegerChanged(int newValue)
{
if (mObsInt.get()==1)
//Do something if the first asyncTask finishes
if (mObsInt.get()==2){
//Do something if the second asyncTask finishes, in this case i just go to another activity when both asyncTasks finish
Intent mainIntent = new Intent().setClass(LoginActivity.this, Principal.class);
startActivity(mainIntent);
finish();
}
}
});
So, how it works
ObservableInteger will be looking for changes in the variable mObsInt, so lets say if mObsInt is equal to 1 it will do something, if is equal to 2 will do another thing, so, to solve this problem with 2 asynctasks is easy, when one of the asynctasks finishes mObsInt will be equal to 1 , if the other asyncTask finishes so mObsInt will be mObsInt++ , and then your mObsInt will be equal to 2, the listener will be waiting for the values, and then do what you want to do when the values match your if statment at the onCreate method
now, just in your asynctasks just put in your onPostExecute() method this line
mObsInt.set(mObsInt.get()+1);
so if the first async finish, mObsint == 1 , if the second finish mObsInt == 2, and then you handle what you want to do in your onCreate method
hope this helps for you, it helped me
You can get more info at this doc : https://developer.android.com/reference/android/databinding/ObservableInt.html
happy coding !