我在运行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

如何解决此问题?


当前回答

解决这个问题还有另一种非常方便的方法——使用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有一本很棒的电子书。

其他回答

您可以使用Kotlin协程:

 class YoutActivity : AppCompatActivity, CoroutineScope {
      
      override fun onCreate(...) {
         launch {  yourHeavyMethod() }
      }

      suspend fun yourHeavyMethod() {
         with(Dispatchers.IO){ yourNetworkCall() }
         ...
         ...
      }
 } 

您可以遵循本指南。

截至2018年,我建议在Kotlin中使用RxJava进行网络获取。下面是一个简单的例子。

Single.fromCallable {
        // Your Network Fetching Code
        Network.fetchHttp(url) 
    }
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe {
        // What you need to do with your result on the view 
        result -> view.updateScreen(result) 
    }

解决这个问题还有另一种非常方便的方法——使用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有一本很棒的电子书。

如果您在Kotlin和Anko工作,您可以添加:

doAsync {
    method()
}

不能在主线程或UI线程上调用网络。在Android上,如果你想呼叫网络,有两个选项-

调用asynctask,它将运行一个后台线程来处理网络操作。您可以创建自己的可运行线程来处理网络操作。

就我个人而言,我更喜欢异步任务。有关详细信息,请参阅此链接。