Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return data from AsyncTask Android

I tried to refer similar question on SO, but didn't got any help.

In my android app, I'm planning to implement Recent Quote the user has visited i.e. similar to recently visited pages on web.

Following are the steps I'm following:

1.) Whenever user opens any company view, fetch the company symbols from database

2.) Then store the current symbol along with dateTime in database.

3.) For all symbols fetched from database, Fetch their current value and %Change and display Company name, current value and %Change in a list.

The problem arises in the ASyncTask class as postExecute method doesn't allow it's return type to be any other than void.

Am I doing anything wrong?

Any help will be life saver !!!

String[] rsym,rcmp,rchg;
rdbM = new RecentDBManager(CompanyView.this);
try {
Calendar date1 = Calendar.getInstance();
SimpleDateFormat dateformatter = new SimpleDateFormat(
                            "dd/MM/yyyy HH:mm:ss");

String currentdate = dateformatter.format(date1.getTime());

rdbM.openDB();

//STEP 1
rsym = rdbM.getRecent_sym();

//STEP 2                
rdbM.setData(currentsymbol, currentdate);

rdbM.closeDB();

} catch (Exception e) {
throw new Error(" *** ERROR in DB Access *** "+ e.toString());
}

 //STEP 3
        for(int i=0;i<rsym.length;i++)
        {
            DownloadRecentQuote quotetask = new DownloadRecentQuote();
            recentquotetask
            .execute(new String[] { "http://abc.com/stockquote.aspx?id="
                        + rsym[i] });

 //CURRENT VALUE and %CHANGE which should be returned from ASyncTask class

            rcmp[i]=valuearr[0];
            rchg[i]=valuearr[1];
            }

            list1 = new ArrayList<HashMap<String, String>>();
            HashMap<String, String> addList1;

            for (int i = 0; i < limit; i++) 
            {
                addList1 = new HashMap<String, String>();
                addList1.put(RecentSym_COLUMN, rsym[i]);
                addList1.put(RecentCMP_COLUMN, rcmp[i]);
                addList1.put(RecentChg_COLUMN, rchg[i]);

                list1.add(addList1);

                RecentAdapter adapter1 = new RecentAdapter(
                    CompanyView.this, CompanyView.this, list1);
                listrecent.setAdapter(adapter1);
            }


private class DownloadRecentQuote extends AsyncTask<String, Void, String> {
    /* Fetching data for RecentQuote information */
    @Override
    protected String doInBackground(String... urls) {
        String response = "";
        for (String url : urls) {
            DefaultHttpClient client = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(url);
            try {
                HttpResponse execute = client.execute(httpGet);
                InputStream content = execute.getEntity().getContent();

                BufferedReader buffer = new BufferedReader(
                        new InputStreamReader(content));
                String s = "";
                while ((s = buffer.readLine()) != null) {
                    response += s;
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return response;
    }

    @Override
    protected void onPostExecute(String result) {

        arr1 = result.split("@");

        if (arr1[0].length() != 0) {

            if (arr1[0].equals("1")) {

                arr = arr1[1].split(";");

            //RETURN 2 STRINGS
                            String valuearr[];
                valuearr[0] = arr[3];
                valuearr[1] = arr[6].concat("%");
                //return valuearr;
            }
        }
    }
like image 655
GAMA Avatar asked Jan 19 '12 05:01

GAMA


3 Answers

postExecute() can't return a value because who or what would it return to? Your original method that invoked the AsyncTask is gone because your AsyncTask is running in the background. It's asynchronous meaning when AsyncTask.execute() returns it's still running in the background, and hence postExecute() can't return a value because there's nothing to return it to.

Instead your AsyncTask needs a reference back to your Activity or some other object so it can post your values back to it. In your code the lines after you call execute() can't be there because your task hasn't finished. Instead you should create a method called updateSymbol( currentPrice, percentChange), move all that code below execute() in there, and in your AsyncTask you should pass a reference to the Activity. Then call updateSymbol( currentPrice, percentChange ) from the onPostExecute() method.

But, be careful if you have a reference back to an Activity it can be destroyed while your doInBackground() is running, and when postExecute() runs it should just drop the results or not attempt to update the UI. For example, the user rotates their phone causing the Activity to be destroyed. I find it best to hold a reference to the AsyncTask in the activity so it can cancel() it if the Activity is destroyed. You can call AsyncTask.cancel() then check if your task was canceled like:

public void postExecute( String result ) {
    if( !isCanceled() ) {
       // do your updating here
       activity.setSymbol( result );
    }
}

It's really easy to create a base class for all Activities so you can easily keep track of AsyncTasks running:

public class BaseActivity extends Activity {

   List<AsyncTask> runningTasks;

   public void onStop() {
       for( AsyncTask task : runningTasks ) {
          task.cancel(true);
       }
   }

   public AsyncTask start( AsyncTask task ) {
      runningTasks.add( task );
      return task;
   }

   public void done( AsyncTask task ) {
      runningTasks.remove( task );
   }
}

Some quick pointers. You don't need execute( new String[] { "blah" + blah } ). Varargs in Java allow you to do this. execute( "blah" + blah ). You also are catching exceptions and continuing without really handling them. It will be hard when something really happens because your app catches them, and just continues as if nothing happened. If you get an error you might want to provide some feedback to the user and stop trying to execute that process. Stop, show an error to the user, and let them do the next thing. Move the catch blocks to the bottom of the methods.

like image 118
chubbsondubs Avatar answered Oct 29 '22 09:10

chubbsondubs


Essentially, AsyncTask.onPostExecute() is where you do whatever you want to do after AsyncTask's doInBackground() is executed and the execution result gets returned. This should be considered the best practice.

When AsyncTask().execute() is called from the UI thread (note that this method must be called from the UI thread), the Android framework creates a worker thread and starts running whatever you wrote in AsyncTask.doInBackground() on this worker thread. At this point (after calling new AsyncTask().execute()), the UI thread continues to execute code after new AsyncTask().execute(). So now during run time, you have two threads (UI and worker thread) both running simultaneously.

But where and when does the AsyncTask execution result get returned from the worker thread back to the UI thread?

The point where your worker thread (doInBackground()) finishes and returns to the UI thread is AysncTask.onPostExecute(). This method is guaranteed to be called by the framework on the UI thread as soon as AsyncTask finishes. In other words, we don't care where and when AsyncTask.onPostExecute() gets called at run time, we just need to guarantee it will be called ultimately at some stage in the future. This is the reason why this method does not return an execution result - instead, it requires that the execution result gets passed in as the only method parameter from doInBackground().

In addition, the Android API provides a way to return an AsyncTask execution result at coding time, AsyncTask.get():

MyAsyncTask myAsyncTask = new MyAsyncTask();

// This must be called from the UI thread:
myAsyncTask.execute();

// Calling this will block UI thread execution:
ExecutionResult result = myAsyncTask.get();

Bear in mind that AsyncTask.get() will block the calling thread's execution, and you will probably get an ANR exception if you call it on the UI thread. This is the payload of using AsyncTask.get(), by calling it on the UI thread, you are actually making your AsyncTask (worker thread) run synchronously with UI thread (by making UI thread wait). To sum up, this is doable but not recommended.

like image 34
yorkw Avatar answered Oct 29 '22 09:10

yorkw


Just for future reference, because this post is a little old:

I have created an Activity class which has an onStart() method and a separate class for the AsyncTask. Based on my test, after the doInbackground() method the result will be sent to the activity first and after that onPostExecute() will run. This is because based off of logcat, I have my first response data (sent by server) first, then this response will show again from the activity and the last the message in onPostExecute() will show.

Code for the activity:

@Override
    protected void onStart() {
        super.onStart();
        String str = "***";
        if(isConnectedToInternet()){
            myAsyncTask.execute();

            try {
                if(myAsyncTask.get())
                    str = myAsyncTask.getResponseMsg();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (CancellationException e) {
                e.printStackTrace();
            }
        }
        Log.i("Data returned by server2:", str);
    }

AsyncTask code:

public class MyAsyncTask extends AsyncTask<Void, Void, Boolean> {

    private URL url;
    private HttpURLConnection conn;
    private String strResponseMsg;

    public MyAsyncTask(String url) throws MalformedURLException{
        this.url = new URL(url);
    }


    @Override
    protected void onPreExecute() {
        Log.i("Inside AsyncTask", "myAsyncTask is abut to start...");
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        boolean status = false;

        try {
            conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(Manager.ConnTimeout);
            conn.setReadTimeout(Manager.ReadTimeout);

            int responseCode = conn.getResponseCode();
            Log.i("Connection oppened", "Response code is:" + responseCode);
            if (responseCode == HttpURLConnection.HTTP_OK) {
                BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                if (in != null) {
                    StringBuilder strBuilder = new StringBuilder();
                    // Read character by character              
                    int ch = 0;
                    while ((ch = in.read()) != -1)
                        strBuilder.append((char) ch);

                    // Showing returned message
                    strResponseMsg = strBuilder.toString(); 
                    Log.i("Data returned by server:", strResponseMsg);

                    status = true;
                }
                in.close();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }       

        return status;
    }

    @Override
    protected void onPostExecute(Boolean result) {
        Log.i("Inside AsyncTask", "myAsyncTask finished its task. Returning data to caller...");
    }

    public String getResponseMsg(){
        return strResponseMsg;
    }
}
like image 28
Hesam Avatar answered Oct 29 '22 08:10

Hesam