0

I currently have a Pollen class which is a class I wrote which uses the JSoup library to parse HTML. Also shown here is my PlaceholderFragment class.

    public static class PlaceholderFragment extends Fragment
    {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState)
        {
            final Button button = (Button) rootView.findViewById(R.id.button1);
            final TextView textview = (TextView) rootView.findViewById(R.id.textview1);

            button.setOnClickListener(new View.OnClickListener()
            {

                @Override
                public void onClick(View v)
                {
                    Pollen pollenObject = new Pollen(19104);
                    textview.setText(pollenObject.getCity());
                }
            });

            return rootView;
        }
    }

Here is my Pollen class.

public static class Pollen
{

    @SuppressLint("SimpleDateFormat")
    public Pollen(int zipcode)
    {
        this.zipcode = zipcode;
        Document doc;
        try
        {
            // pass address to 
            doc = Jsoup.connect("http://www.wunderground.com/DisplayPollen.asp?Zipcode=" + this.zipcode).get();

            // get "location" from XML
            Element location = doc.select("div.columns").first();
            this.location = location.text();

            // get "pollen type" from XML
            Element pollenType = doc.select("div.panel h3").first();
            this.pollenType = pollenType.text();

            SimpleDateFormat format = new SimpleDateFormat("EEE MMMM dd, yyyy");

            // add the four items of pollen and dates
            // to its respective list
            for(int i = 0; i < 4; i++)
            {
                Element dates = doc.select("td.text-center.even-four").get(i);
                Element levels = doc.select("td.levels").get(i);

                try
                {
                    pollenMap.put(format.parse(dates.text()), levels.text());
                }
                catch (ParseException e)
                {
                    e.printStackTrace();
                }
            }
        }
        catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

I learned a lot while failing every attempt. I discovered that, in my use of calling my Pollen class within onClick, I am doing an expensive task. Thus, I should put it in a separate thread. To add, since I am calling my Pollen class within my main/UI thread, it causes my app to crash.

I consulted this Stackoverflow question in aiding my attempt to solve my issue: How to fix android.os.NetworkOnMainThreadException?

I discovered my error and solution via this error log from logcat, specifically, NetworkOnMainThread where Android explicitly prevents me from doing anything network on my UI thread.

My question is - how do I allocate my Pollen class into a separate thread that is not in my UI class?

enter image description here

Continuing my tutorial on that Stackoverflow thread, I have added this class. I have no idea what I am doing.. But I will try my best to continue:

abstract class RetrievePollenTask extends AsyncTask<Integer, Void, Pollen>
{

    protected Pollen doInBackground(String... params)
    {
        // TODO Auto-generated method stub
        return null;
    }

}
Community
  • 1
  • 1
theGreenCabbage
  • 5,197
  • 19
  • 79
  • 169

5 Answers5

1

Yes you are trying to do networking inside the UI thread which is illegal and catch an exception which is NetworkOnMainThreadException.

Instead of connecting inside the main thread you can use the AsyncTask which you are doing right now but it shouldn't be abstract it is just a plain class so you can execute the AsyncTask..

example:

    public class RetrievePollenTask extends AsyncTask<Integer, Void, Pollen>
{

    protected Pollen doInBackground(String... params)
    {
        Pollen pollenObject = new Pollen(19104);
        return pollenObject;
    }

    protected void onPostExecute(Pollen result) {
         textview.setText(pollenObject.getCity());
     }

}

you can make your asynctask as an inner class of you fragment so you dont need to pass the context parameter on it. Also dont put update any view insidte the doInBackground cause it is a different thread that it will catch an exception.

Rod_Algonquin
  • 26,074
  • 6
  • 52
  • 63
  • Hi Rod. Thank you very much for your reply. I assume the `RetrievePollenTask` goes inside my `Fragment` class am I right? I cannot access the `textview` object without doing so. – theGreenCabbage May 31 '14 at 19:02
  • @theGreenCabbage if you put the textView as a global variable so you can reference it through the inner class or create a contructor inside the Asynctask and pass your textview as a parameter – Rod_Algonquin May 31 '14 at 19:03
0

This is a tutorial of AsyncTask. You have to do all the network operations using AsyncTask. It basically has 3 methods

  • onPreExecute()
  • doInBackground()
  • onPostExecute()

do the network operations in doInBackground() method. update the UI in onPostExecute()

class RetrievePollenTask extends AsyncTask<Integer, Void, Pollen>
{
    @Override
    protected Pollen doInBackground(Integer zip)
    {
         // TODO Auto-generated method stub
         Pollen pollenObject = new Pollen(zip);
        return pollenObject;
    }
    @Override
    protected void onPostExecute(Pollen pollenObject) {
        textView.setText(pollenObject.getCity());
    }

}

And inPlacholderFragment class

button.setOnClickListener(new View.OnClickListener()
        {

            @Override
            public void onClick(View v)
            {
                RetrievePollenTask object = new RetrievePollenTask();
                object.execute(19104);
            }
        });
MjZac
  • 3,476
  • 1
  • 17
  • 28
0

AsyncTask is a mechanism provided by the Android framework to facilitate execution of long running tasks without blocking the main UI thread. Read Keeping Your Apps Responsive tutorial on the Android Developer sites for a detailed treatment on this subject.

This link provided should help you re-design your classes using the AsyncTask mechanism.

Hope this helps

Prahalad Deshpande
  • 4,709
  • 1
  • 20
  • 22
0

An AsyncTask is used to run code in a non UI thread but also has methods that can interact with your UI.

AsyncTask<String, Integer, Pollen>
This defines the Objects that the AsyncTask uses. Use Void if no object is to be used.

  • The first is an input for your doInBackground().
  • The second is an argument for onProgressUpdate() to be used to periodically update the user on progress
  • The last is the result returned from doInBackground()

These Arguments are declared with Object... vars and are actually arrays that you would access with vars[index]. If you only ever pass one variable access it with var[0]

Here is the official documentation

class RetrievePollenTask extends AsyncTask<String, Integer, Pollen>
{

    @Override
    protected void onPreExecute() {
        // set up a ProgressBar or other UI action here *before* the task runs
    }

    @Override
    protected Pollen doInBackground(String... params)
    {
        // perform your separate non UI thread actions here
        // call publishProgress(int) to update your UI periodically
        return pollenObject;
    }

    @Override
    protected void onProgessUpdate(Integer... progress) {
        // update your UI or ProgressBar
    }

    @Override
    protected void onPostExectute(Pollen resultReturned)
        // back to the UI thread again here to update Views or cancel your ProgressBar etc
        // this executes *after* your task has completed
        // it receives its argument from the return of doInBackground() so you can use that when updating your UI.
    }
}
indivisible
  • 4,892
  • 4
  • 31
  • 50
0

Rule of thumb:

  • Network (or other I/O or long-running) code goes in doInBackground(), which is executed in a background thread.
  • UI code goes in onPostExecute(), which is executed o the UI thread as soon as the background operation is finished.

In your example, this is probably the simplest code you could write:

button.setOnClickListener(new View.OnClickListener()
{
    @Override
    public void onClick(View v)
    {
        AsyncTask<Integer, Void, Pollen> loadPollen = new AsyncTask<Integer, Void, Pollen>()
        {
            @Override
            protected Pollen doInBackground(Integer... params)
            {
                return new Pollen(params[0]);
            }

            @Override
            protected void onPostExecute(Pollen result)
            {
                textview.setText(result.getCity());
            }
        };

        loadPollen.execute(19104);
    }
});     
matiash
  • 54,791
  • 16
  • 125
  • 154
  • Oh my God. Thank you SO much. You solved a problem that took me 3 days to figure out. Thank you so much. Do you mind explaining to me what and how you decided to do what you did? – theGreenCabbage May 31 '14 at 19:06
  • Sorry, but I didn't understand... do you mean "about using AsyncTask" or "about this particular code snippet"? – matiash May 31 '14 at 19:09