-1

I wrote a JSoup HTML scraping class for my Android project. This custom class puts the user-inputted zip code into the constructor, and would parse that HTML. It works in an asynchronous thread from my main thread. Since there is no right way to deal with incorrect zip codes, I had check for null in a particular element, specifically:

        if(doc.select("div.columns").first().text() == null)
        {
            ((Activity) context).runOnUiThread(new Runnable() {
                public void run() 
                {
                    Toast toast = Toast.makeText(context, R.string.toast_parse_fail, Toast.LENGTH_LONG);
                    toast.show();
                    return;
                }
            });
        }

If that particular element is null (meaning that no such data exists for this zip code), it would create a toast to the user. As to why try wouldn't work in this case, it is because JSoup and Java doesn't know whether the parse failed or not, since the web page still loads fine; only, there is no data, which would crash the app from a NullPointerException from the code following it.

Despite my Toast exception handling using .runOnUiThread, my app would still crash. Is there any particular methods that would cancel the operations that follow my null-checking method? I know I am crashing because Toast is not cancelling my operations, and is preceding to execute the following code which causes my NullPointerExceptions.

Posted is my full Pollen constructor.

        public Pollen(int zipcode, final Context context)
        {
            this.context = context;
            this.zipcode = zipcode;
            Document doc;

            try
            {
                // pass address to Wunderground's website using our inputted zipcode
                doc = Jsoup.connect("http://www.wunderground.com/DisplayPollen.asp?Zipcode=" + this.zipcode).get();

                if(doc.select("div.columns").first().text() == null)
                {
                    ((Activity) context).runOnUiThread(new Runnable() {
                        public void run() 
                        {
                            Toast toast = Toast.makeText(context, R.string.toast_parse_fail, Toast.LENGTH_LONG);
                            toast.show();
                            return;
                        }
                    });
                }

                // 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)
                    {
                        ((Activity) context).runOnUiThread(new Runnable() {
                            public void run() 
                            {
                                Toast toast = Toast.makeText(context, R.string.toast_parse_fail, Toast.LENGTH_LONG);
                                toast.show();
                                return;
                            }
                        });
                    }
                }
            }
            catch (IOException e)
            {
                ((Activity) context).runOnUiThread(new Runnable() {
                    public void run() 
                    {
                        Toast toast = Toast.makeText(context, R.string.toast_parse_fail, Toast.LENGTH_LONG);
                        toast.show();
                        return;
                    }
                });
            }
        }

tl;dr I still want the Toast to show - but I want on-Toast, to cancel all operations that follow it, since I know that if this particular Toast shows, if the code following it executes, it would crash the app.

theGreenCabbage
  • 5,197
  • 19
  • 79
  • 169
  • Refer to this thread on [How to cancel Toast][1] [1]: http://stackoverflow.com/questions/4395062/how-to-cancel-toast – user1406716 Jun 01 '14 at 23:56
  • I don't mean to cancel the `Toast`. I still want the `Toast` to show - but I want on-Toast, to cancel all operations that follow it, since I know that if this particular `Toast` shows, if the code following it executes, it would crash the app. – theGreenCabbage Jun 01 '14 at 23:56

1 Answers1

0

Since you already in a try-catch-block, can't you just throw an exception?

public Pollen(int zipcode, final Context context) {
    this.context = context;
    this.zipcode = zipcode;
    Document doc;

    try {
       // pass address to Wunderground's website using our inputted zipcode
       doc = Jsoup.connect("http://www.wunderground.com/DisplayPollen.asp?Zipcode=" + this.zipcode).get();

       if(doc.select("div.columns").first().text() == null) {
           // Oh no! div.colums is empty. Lets throw an exception, which
           // will prevent the code below from executing.
           throw new IllegalStateException("div.columns is NULL");
       }

       // 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);

           // Removed nested try-catch block
           pollenMap.put(format.parse(dates.text()), levels.text());
       }
    } catch (IOException e) {
        displayToast(context);
    } catch (ParseException e) {
        // We catch the ParseException here instead of nesting try-catch blocks.
        displayToast(context);
    } catch (IllegalStateException e) {
        // Catch the IllegalStateException thrown when div.columns was null,
        // and let the user know what went wrong.
        displayToast(context);
    }
}

private void displayToast(Context context) {
    ((Activity) context).runOnUiThread(new Runnable() {
        public void run() {
            Toast toast = Toast.makeText(context, R.string.toast_parse_fail, Toast.LENGTH_LONG);
            toast.show();
        }
    });
}
Ole
  • 7,899
  • 2
  • 29
  • 34
  • I have put in your recommended fixes, however, it still seems to crash. – theGreenCabbage Jun 02 '14 at 00:28
  • It doesn't seem the try-catch with `Toast` is preventing the crash-behavior from executing, since it's still trying to access my array, which, knowingly, is empty. – theGreenCabbage Jun 02 '14 at 00:29
  • Actually! I put in a catch for the ArrayIndexOutOfBoundsError, and it worked! Thank you! – theGreenCabbage Jun 02 '14 at 00:31
  • I learned something about logcat. If logcat is displaying that particular exception, I should put that in a catch. Thanks a lot again Ole. – theGreenCabbage Jun 02 '14 at 00:35
  • 1
    @theGreenCabbage Good :) As a side note; it is considered bad practice to do a lot of work in the constructor of a class. – Ole Jun 02 '14 at 00:40
  • 1
    1) Having a constructor report errors by sending stuff to the GUI is a really bad idea. Do that outside of the constructor. 2) What you are currently doing will result in the constructor delivering partly constructed (i.e. broken) objects ... – Stephen C Jun 08 '14 at 03:12