Personal Jesus
Personal Jesus

Reputation: 1309

Load document to WebView with JSOUP

I'm trying to parse a part of webpage to WebView. I'm using jsoup library to get part of page that i need, and then load to webview. Here is code:

public void loadArticleWithHTML (){
    Thread downloadThread = new Thread() {
        public void run() {
            try {
                doc = Jsoup.connect("http://en.wikipedia.org/").get();
                element = doc.select("#mp-itn b a");

            } catch (java.io.IOException e){
                e.printStackTrace();
            }
        }
    };
    downloadThread.start();

    mWebView.setWebViewClient(new WebViewClient() {
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            Toast.makeText(getApplicationContext(), description, Toast.LENGTH_SHORT).show();
        }
    });
    try {
        mWebView.loadData(element.html(), "text/html", "UTF-8");
    } catch (NullPointerException e){
        e.printStackTrace();
        Toast.makeText(getApplicationContext(), "error", Toast.LENGTH_LONG).show();
    }

}

But I always get an error

Attempt to invoke virtual method 'java.lang.String org.jsoup.select.Elements.html()' on a null object reference

Upvotes: 4

Views: 4369

Answers (2)

anthonycr
anthonycr

Reputation: 4186

Your problem is that you are using a thread to download and parse the HTML content (that's the correct thing to do) and then you are trying to load from the Element object outside the thread. Because downloading the page takes some time to finish, you are calling element.html() before it has been initialize and is therefore null - which is why you are getting a NullPointerException.

To explain what's going on, lets look at the flow of your loadArticleWithHtml method:

  1. You create a thread that is supposed to download and parse HTML
  2. You start the thread and the downloading of the page probably starts
  3. You set the WebViewClient
  4. You load data into the WebView and try to access element.html() (element has not been initialized yet and is still null) and get a Null Pointer Exception
  5. Sometime after, the page downloading finishes and element is initialized

I suggest you read more about threading. When you use a thread, the process runs in parallel to the UI thread (which is where you are loading the HTML) and is not guaranteed to finish before the rest of your code in the UI thread does. In fact, doing work on the UI thread and starting a thread in the middle of it, it is almost guaranteed that the thread will finish after the UI code finishes if the code is doing anything slow like downloading.

So, the solution is to correctly thread your application and load the WebView AFTER the element variable has been initialized from within the thread. See below.

public void loadArticleWithHTML (){
    Thread downloadThread = new Thread() {
        public void run() {
            try {
                doc = Jsoup.connect("http://en.wikipedia.org/").get();
                element = doc.select("#mp-itn b a");

            } catch (java.io.IOException e){
                e.printStackTrace();
            }
            if (element == null) {
                Log.e("error", "There is a problem with the selection");
            } else {
                // post a new Runnable from a Handler in order to run the WebView loading code from the UI thread
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        mWebView.loadData(element.html(), "text/html", "UTF-8");
                    }
                });
            }
        }
    };

    mWebView.setWebViewClient(new WebViewClient() {
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            Toast.makeText(getApplicationContext(), description, Toast.LENGTH_SHORT).show();
        }
    });

    downloadThread.start();
}

Note, you need to run the WebView method from the UI thread as it is a view and should be accessed from the main thread. See this Q/A for additional info on running code on the UI thread.

Upvotes: 5

luksch
luksch

Reputation: 11712

I think you are not aware of the fact that

doc.select("#mp-itn b a")

will return with Elements, i.e. a collection of matching Element nodes, despite your usage of # in the CSS selector, which should indeed only return one Element. If you use

doc.select("#mp-itn b a").first()

it at least gets the Element with the id you want. Maybe there is more issues to your code though, I did not check for that.

Addendum

i am not very familiar with Android development, but the error you are getting points towards a null pointer issue, indicating that element is null where you try to invoke element.html()

Upvotes: 0

Related Questions