使用JSOUP将文档加载到WebView中

3

我正在尝试将网页的一部分解析到WebView中。我使用jsoup库获取所需的页面部分,然后加载到webview中。 以下是代码:

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();
    }

}

但我总是遇到错误

尝试在空对象引用上调用虚拟方法“java.lang.String org.jsoup.select.Elements.html()”

2个回答

4
你的问题在于你正在使用一个线程来下载和解析HTML内容(这是正确的做法),然后你试图从线程外部加载Element对象。因为下载页面需要一些时间才能完成,所以你在初始化之前调用了element.html(),因此它为空 - 这就是为什么你会收到NullPointerException的原因。
为了解释发生了什么,让我们看一下你的loadArticleWithHtml方法的流程:
  1. 创建一个线程来下载和解析HTML
  2. 启动线程并开始下载页面
  3. 设置WebViewClient
  4. 将数据加载到WebView中并尝试访问element.html()(element尚未初始化,仍然为空),并获取Null Pointer Exception
  5. 在某个时候,页面下载完成,element被初始化
我建议您阅读有关线程的更多信息。当您使用线程时,进程会与UI线程(即您正在加载HTML的地方)并行运行,并不保证在UI线程中的其余代码完成之前结束。实际上,如果在UI线程上工作并在其中间启动线程,如果代码执行像下载这样缓慢的操作,几乎可以保证线程将在UI代码完成之后结束。

因此,解决方案是正确地对应用程序进行线程处理,并在元素变量从线程内部初始化后再加载WebView。请参见下文。

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();
}

注意,由于WebView是一个视图并且应该从主线程访问,因此需要从UI线程运行WebView方法。有关在UI线程上运行代码的其他信息,请参见this Q/A

谢谢,但现在我遇到了错误 A WebView method was called on thread 'Thread-159'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {b10dae3} called on null, FYI main Looper is Looper (main, tid 1) {b10dae3})。我已经修改了代码 像这样。看起来现在它可以工作了。 - Personal Jesus
@user2496887 哦,对了,我会在我的答案中进行更正。你需要从UI线程访问WebView。 - anthonycr

0

我认为你没有意识到这个事实

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

将返回元素,即匹配的元素节点集合,尽管您在CSS选择器中使用了#,这应该只返回一个元素。如果您使用

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

它至少可以获取您想要的ID元素。也许您的代码还有更多问题,但我没有检查。

补充说明

我不太熟悉Android开发,但您遇到的错误指向了一个空指针问题,表明在尝试调用element.html()element为空。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接