我在运行RssReader的Android项目时出错。

代码:

URL url = new URL(urlToRssFeed);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLReader xmlreader = parser.getXMLReader();
RssHandler theRSSHandler = new RssHandler();
xmlreader.setContentHandler(theRSSHandler);
InputSource is = new InputSource(url.openStream());
xmlreader.parse(is);
return theRSSHandler.getFeed();

它显示以下错误:

android.os.NetworkOnMainThreadException

如何解决此问题?


当前回答

永远不要在UI线程上执行任何长时间运行的工作。长时间运行的工作可以是与服务器通信、读取/写入文件等。这些任务应该在后台线程上。这就是创建服务、异步任务和线程的原因。您可以禁用StrictMode,这将防止崩溃。但是,这是绝对不建议的。

我建议您至少在调试模式下利用StrictMode。使用下面的代码获取主线程上减慢应用程序速度的任何问题的日志。

StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
            .detectAll()
            .penaltyLog()
            .build());

您可以设置不同的惩罚:

penaltyLog() // to print log
penaltyDeath() // This will crash you App(so costly penalty)
penaltyDialog() // Show alert when something went lazy on Main thread

这里有更多关于StrictMode的信息:StrictMode|Android开发者。

其他回答

不允许在Android上的UI线程上实现网络操作。您将不得不使用AsyncTask类来执行与网络相关的操作,如发送API请求、从URL下载图像等,并使用AsyncTask的回调方法,您可以在onPostExecute中获得结果,您将处于UI线程中,您可以使用web服务或类似的数据填充UI。

示例:假设您想从URL下载图像:https://www.samplewebsite.com/sampleimage.jpg

使用AsyncTask的解决方案:分别为。

    public class MyDownloader extends AsyncTask<String,Void,Bitmap>
    {
        @Override
        protected void onPreExecute() {
            // Show progress dialog
            super.onPreExecute();
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            //Populate Ui
            super.onPostExecute(bitmap);
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            // Open URL connection read bitmaps and return form here
            return result;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            // Show progress update
            super.onProgressUpdate(values);
        }


    }
}

注意:不要忘记在Android清单文件中添加Internet权限。它会像一个魅力一样发挥作用。:)

解决这个问题还有另一种非常方便的方法——使用RxJava的并发功能。您可以在后台执行任何任务,并以非常方便的方式将结果发布到主线程,因此这些结果将被传递到处理链。

第一个经过验证的答案建议是使用AsynTask。是的,这是一个解决方案,但现在已经过时了,因为周围有新的工具。

String getUrl() {
    return "SomeUrl";
}

private Object makeCallParseResponse(String url) {
    return null;
    //
}

private void processResponse(Object o) {

}

getUrl方法提供URL地址,它将在主线程上执行。

makeCallParseResponse(..)-执行实际工作

processResponse(..)-将处理主线程上的结果。

异步执行的代码如下:

rx.Observable.defer(new Func0<rx.Observable<String>>() {
    @Override
    public rx.Observable<String> call() {
        return rx.Observable.just(getUrl());
    }
})
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.io())
    .map(new Func1<String, Object>() {
        @Override
        public Object call(final String s) {
            return makeCallParseResponse(s);
        }
    })
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Action1<Object>() {
        @Override
        public void call(Object o) {
             processResponse(o);
        }
    },
    new Action1<Throwable>() {
        @Override
        public void call(Throwable throwable) {
            // Process error here, it will be posted on
            // the main thread
        }
    });

与AsyncTask相比,此方法允许切换调度器任意次数(例如,在一个调度器上获取数据,并在另一个调度器中处理这些数据(例如,scheduler.computation()))。您还可以定义自己的调度器。

为了使用此库,请在build.gradle文件中包含以下行:

   compile 'io.reactivex:rxjava:1.1.5'
   compile 'io.reactivex:rxandroid:1.2.0'

最后一个依赖项包括对.mainThread()调度程序的支持。

RxJava有一本很棒的电子书。

您不能在蜂巢上的UI线程上执行网络I/O。从技术上讲,这在早期版本的Android上是可能的,但这是一个非常糟糕的想法,因为它会导致你的应用停止响应,并可能导致操作系统因你的应用行为不端而杀死你的应用。您需要运行后台进程或使用AsyncTask在后台线程上执行网络事务。

Android开发者网站上有一篇关于“无痛苦线程”的文章,这是一篇很好的介绍,它将为您提供比这里实际提供的答案更深入的答案。

注意:AsyncTask在API级别30中已弃用。AsyncTask | Android开发人员

当应用程序尝试在其主线程上执行网络操作时,会引发此异常。在AsyncTask中运行代码:

class RetrieveFeedTask extends AsyncTask<String, Void, RSSFeed> {

    private Exception exception;

    protected RSSFeed doInBackground(String... urls) {
        try {
            URL url = new URL(urls[0]);
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser parser = factory.newSAXParser();
            XMLReader xmlreader = parser.getXMLReader();
            RssHandler theRSSHandler = new RssHandler();
            xmlreader.setContentHandler(theRSSHandler);
            InputSource is = new InputSource(url.openStream());
            xmlreader.parse(is);

            return theRSSHandler.getFeed();
        } catch (Exception e) {
            this.exception = e;

            return null;
        } finally {
            is.close();
        }
    }

    protected void onPostExecute(RSSFeed feed) {
        // TODO: check this.exception
        // TODO: do something with the feed
    }
}

如何执行任务:

在MainActivity.java文件中,可以在oncreate()方法中添加此行

new RetrieveFeedTask().execute(urlToRssFeed);

不要忘记将其添加到AndroidManifest.xml文件中:

<uses-permission android:name="android.permission.INTERNET"/>

您几乎应该始终在线程上或作为异步任务运行网络操作。

但如果您愿意接受后果,则可以取消此限制,并覆盖默认行为。

Add:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();

StrictMode.setThreadPolicy(policy);

在你的课堂上,

and

在Android manifest.xml文件中添加此权限:

<uses-permission android:name="android.permission.INTERNET"/>

后果:

你的应用程序(在互联网连接不稳定的地区)将变得无响应并被锁定,用户感觉到速度缓慢,必须进行强制关闭,你冒着活动管理器关闭你的应用并告诉用户应用程序已停止的风险。

Android提供了一些关于良好编程实践的好建议,以设计响应能力:NetworkOnMainThreadException | Android开发人员