30

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

hcpeter
  • 614
  • 3
  • 11
  • 24
  • Are you sure that the auth task really has finished. Maybe the freeze is because of a long http timeout? – Renard Apr 06 '12 at 20:43
  • I assume you've debugged the bits in `doInBackground` that can hang nearly indefinitely? Are you using time-outs that are reasonable when calling your REST API? Typically the only requirement for an `AsyncTask` is that you create it on the UI thread which you are doing. – Jens Apr 06 '12 at 20:46
  • Yes I tried to debug and I can't reproduce the problem while debugging... The other thing is the timeout. I set reasonable timeout but this cause and another bug another class. (This is most likely my bad, need further investigation.) – hcpeter Apr 06 '12 at 21:11

7 Answers7

27

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
        }
      }
   }); 
ρяσѕρєя K
  • 132,198
  • 53
  • 198
  • 213
  • Let me try this solution thanks for the example! So I can start 2 separate task a row and when the authTask is finished I can pass through the user. – hcpeter Apr 06 '12 at 21:11
  • yes right in your case this post may help you http://stackoverflow.com/questions/8337419/asynctask-status-always-showing-running keep this situation in mind. – ρяσѕρєя K Apr 06 '12 at 21:24
  • Uhh, sorry for the late response I was sick. I have one problem with this solution: in authTask I get data which is a parameter to imageTask. Somehow I need to know that authTask is finished and I can start the imageTask.. – hcpeter Apr 09 '12 at 14:14
  • this not problem just take and store somewhere parameter from onPostExecute of authTask and when authTask finished pass it to imageTask – ρяσѕρєя K Apr 09 '12 at 15:15
  • Thanks, finally I use this approach and re-struct my application a little bit :) – hcpeter Apr 14 '12 at 19:21
22

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);
Peter Ajtai
  • 56,972
  • 13
  • 121
  • 140
  • All of this is good information. Unfortunately if you want to support pre-HC/ICS, you can't do this. 1. Tasks are not executed serially in those versions. 2. The APIs to specify an executor were not introduced yet. In 1.6 AsyncTask used a thread pool of size 1, but after that it was a core pool size of 5. The only solution is to do something similar to OP if you have to support older versions. – kabuko Apr 06 '12 at 21:29
  • Hello, It would be great but I have to use api level 10. Is it possible to execute some task in a row? – hcpeter Apr 12 '12 at 06:29
  • @hcpeter - Hmm... looks like executeOnExecutor is api level 11. You just have to chain them either in the same doInbackground or call one from the other onPostExecute (like you're already doing). – Peter Ajtai Apr 12 '12 at 17:18
  • Your answer enlightened me as to why on newer versions of the API my tasks are refusing to run in parallel. Seems I was missing THREAD_POOL_EXECUTOR! – Ilya Kogan May 09 '13 at 20:13
5

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.

waqaslam
  • 67,549
  • 16
  • 165
  • 178
  • in fairness, that's apparently what he is doing. – Jens Apr 06 '12 at 20:43
  • His `doInBackground` contains a `// call the rest api` comment. the result of that call, apparently a `JSONObject` is parsed & dispatched to the next step - which in his case is another `AsyncTask`. – Jens Apr 06 '12 at 20:48
  • he doesnt need to pass the result to another task. He can simply process it with in the same task. cant he? – waqaslam Apr 06 '12 at 20:52
  • depends - the behavior for AsyncTask:s were changed a bit for ICS making them sequential by default - i.e. a single long running task would block others until it's finished. – Jens Apr 06 '12 at 20:53
  • thats what i'm saying, he doesnt need to use another task since he can use a single task to achieve api call and set the image – waqaslam Apr 06 '12 at 20:57
1

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.

Alex Gitelman
  • 24,429
  • 7
  • 52
  • 49
1
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();
                }
            }
        }
    }

}
0

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();
        }
}
0

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 !

Gastón Saillén
  • 12,319
  • 5
  • 67
  • 77