0

Am trying to populate RecyclerView by scrapping data from website. Problem is getData() method of SearchCards.java (model) returns String (pageTitle) before AsyncTask has completed execution resulting in empty String. Here is my code

FragmentOne.java

 mAdapter = new SearchAdapter(SearchCards.getData(),this,recyclerView);
 recyclerView.setAdapter(mAdapter);

SearchCards.java

public static ArrayList<SearchCards> getData() {

    ArrayList<SearchCards> datatList = new ArrayList<SearchCards>();

    String pageTitle = new Scrapping().scrapSubs(); //scrapSubs contains AsynTask                     
    String[] yearArr = setYearArr();

    for (int i = 0; i < yearArr.length; i++) {
        SearchCards searchCard = new SearchCards();
        searchCard.setYear(yearArr[i]);
        searchCard.setTitle(pageTitle); //same pagetitle for every year
        datatList.add(searchCard);             
    }      

    return dataList;
}

Scrapping.java

public String ScrapSubs(){
    JsoupAsyncTask jsoupAsyncTask = new JsoupAsyncTask();
    jsoupAsyncTask.execute();
    return pageTitle;
}

private class JsoupAsyncTask extends AsyncTask<Void, Void, Void> {
    @Override
        protected Void doInBackground(Void... params) {
            try {
                htmlDocument = Jsoup.connect(htmlPageUrl).get();
                pageTitle = htmlDocument.title();
                Log.i(TAG,"PAGETITLE"+pageTitle);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

    @Override
        protected void onPostExecute(Void result) {
        //do something
        }
}

How to do something like return pageTitle in Scrapping.java executes only after AsyncTask execution completes. I tried this but didn't work:

if(jsoupAsyncTask.getStatus() == AsyncTask.Status.FINISHED){
     return pageTitle;
}

EDIT: May be this was not a well-researched question. Here I found what I was looking for: http://stacktips.com/tutorials/android/android-recyclerview-example All I had to do was move SearchAdapter initialisation in onPostExecute().

mustafa1993
  • 541
  • 1
  • 5
  • 17
  • `protected void onPostExecute(Void result) { //do something`. Not only something. But for instance call a modified getData() and setup your listview. That is the normal way. Please adapt your design. What you have now is not the way to go at all. – greenapps Mar 18 '17 at 10:08
  • Possible duplicate of http://stackoverflow.com/questions/12575068/how-to-get-the-result-of-onpostexecute-to-main-activity-because-asynctask-is-a – Maximilien Belinga Mar 18 '17 at 10:19
  • Thanks for your input. Am new to android and trying to adapt. Though answers here gave some clarity for me to proceed. – mustafa1993 Mar 18 '17 at 11:04

3 Answers3

2

Create intarface:

private class JsoupAsyncTask extends AsyncTask<Void, Void, Void> {

    public interface FinishListener {
        void processFinish(Boolean isFinish);
    }

    FinishListener finishListener = null;

    public JsoupAsyncTask(FinishListener  finishListener) {
        this.finishListener = finishListener;
    }

    @Override
    protected Void doInBackground(Void... params) {
        try {
            htmlDocument = Jsoup.connect(htmlPageUrl).get();
            pageTitle = htmlDocument.title();
            Log.i(TAG, "PAGETITLE" + pageTitle);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        finishListener.processFinish(true);
        //do something
    }
}

And listener:

new JsoupAsyncTask(new JsoupAsyncTask.FinishListener() {
@Override
public void processFinish(Boolean isFinish) {
        // do somthing  }
        }).execute();
Grzegorz
  • 841
  • 7
  • 9
1

if you want to pass the result of doInBackground while is finished , just define what ever data type you want to pass in 3th AsyncTask parameter type( Integer ).here i want to pass an integer result to onPostExecute in which shows state of operation. 0 failed and 1 successful.

private class JsoupAsyncTask extends AsyncTask<Void, Void, Integer> {
        @Override
        protected int doInBackground(Void... params) {
            try {
                htmlDocument = Jsoup.connect(htmlPageUrl).get();
                pageTitle = htmlDocument.title();
                Log.i(TAG,"PAGETITLE " + pageTitle);
                return 1;
            } catch (IOException e) {
                e.printStackTrace();
                return 0;
            }
        }

        @Override
        protected void onPostExecute(int result){
         //TODO 
         if(result == 1){Log.i("state:","successful");}else{Log.i("state:","failed");}
        }
}
A Farmanbar
  • 4,381
  • 5
  • 24
  • 42
0

If you want your code to run syncrhonously then you dont need to use AsyncTask at all - just create a method that does what you need and call that. This does however not work in UI thread - UI thread methods have to return quickly, otherwise whole app stalls. You probably have to rethink your design. One way to to it is show some kind of "waiting ui", spinner, progressbar etc while your AsyncTask runs in the background, and only when you have data move on and display it.

Okas
  • 2,664
  • 1
  • 19
  • 26
  • I tried doing without AsyncTask. It throws Exception. https://developer.android.com/reference/android/os/NetworkOnMainThreadException.html – mustafa1993 Mar 18 '17 at 11:06
  • Of course it does. You have to learn why it does and also why your idea can not work. – Okas Mar 18 '17 at 11:45
  • Yes, I got it working now. My approach was wrong before. Now I am calling AsyncTask.execute() directly from my Fragment class and initialising RecyclerView in onPostExecute(). As you suggested, I rethink-ed my design. – mustafa1993 Mar 18 '17 at 12:21