我在运行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
如何解决此问题?
29条答案
按热度按时间up9lanfz1#
您不应该在主线程(UI线程)上执行任何耗时的任务,如任何网络操作、文件I/O或SQLite数据库操作。因此,对于这种操作,您应该创建一个工作线程,但问题是您不能从工作线程直接执行任何与UI相关的操作。为此,您必须使用
Handler
并通过Message
。为了简化所有这些,Android提供了多种方式,如
AsyncTask
、AsyncTaskLoader
、CursorLoader
或IntentService
。因此,您可以根据您的需求使用这些。qrjkbowd2#
RxAndroid
是解决这个问题的另一个更好的替代方案,它使我们避免了创建线程,然后在Android UI线程上发布结果的麻烦。我们只需要指定需要执行任务的线程,并且所有任务都在内部处理。
1.通过指定
(Schedulers.io())
,RxAndroid将在不同的线程上运行getFavoriteMusicShows()
。1.通过使用
AndroidSchedulers.mainThread()
,我们希望在UI线程上观察到这个可观察的,即,我们希望我们的onNext()
回调在UI线程中被调用。1sbrub3j3#
主线程是UI线程,您不能在主线程中执行可能会阻止用户交互的操作。您可以通过两种方式解决此问题:
强制在主线程中执行任务,如下所示
或者创建一个简单的处理程序,并根据需要更新主线程。
并停止线程使用:
有关更多信息,请查看:Painless threading
mefy6pfw4#
尽管上面有一个巨大的解决方案池,但没有人提到
com.koushikdutta.ion
:https://github.com/koush/ion它也是异步和非常简单使用:
tpxzln5u5#
这是可行的。我只是让路易吉博士的回答简单一点。
nbysray56#
新的
Thread
和AsyncTask解决方案已经解释过。理想情况下,
AsyncTask
应用于短期操作。普通Thread
不适用于Android。查看使用HandlerThread和Handler的替代解决方案
手线
用于启动具有循环器的新线程的方便类。然后可以使用循环器创建处理程序类。请注意,仍然必须调用
start()
。处理程序:
处理程序允许您发送和处理与线程的MessageQueue关联的消息和可运行对象。每个处理程序示例都与一个线程和该线程的消息队列相关联。当您创建一个新的处理程序时,它将绑定到正在创建它的线程的线程/消息队列——从那时起,它将把消息和可运行项传递到该消息队列,并在它们从消息队列中出来时执行它们。
解决方案:
1.创建
HandlerThread
1.在
HandlerThread
上呼叫start()
1.通过从
HanlerThread
获取Looper
来创建Handler
1.在
Runnable
对象中嵌入您的网络操作相关代码1.将
Runnable
任务提交给Handler
示例代码片段,地址为
NetworkOnMainThreadException
使用这种方法的优点:
1.为每个网络操作创建新的
Thread/AsyncTask
是昂贵的。Thread/AsyncTask
将被销毁并重新创建,用于下一次网络操作。但使用Handler
和HandlerThread
方法,您可以使用Handler
将许多网络操作(作为可运行任务)提交给单个HandlerThread
。w1jd8yoj7#
Kotlin
如果您正在使用Kotlin,您可以使用*coroutine*:
2admgd598#
您可以将代码的一部分移动到另一个线程中,以卸载
main thread
,并避免获取ANR、NetworkOnMainThreadException和IllegalStateException(例如,无法访问主线程上的数据库,因为它可能会长时间锁定UI)。根据情况,您应该选择一些方法
Java Thread或Android HandlerThread:
Java线程是一次性使用的,在执行其run方法后会死亡。
HandlerThread是一个方便的类,用于启动具有循环器的新线程。
AsyncTask(在API 30级中已弃用)
异步任务被设计为围绕线程和处理程序的助手类,并不构成通用线程框架。异步任务理想情况下应用于短时间操作(最多几秒钟)。如果您需要让线程长时间运行,强烈建议您使用java.util提供的各种API。并发包,如执行器、线程池执行器和未来任务。
由于main线程垄断了UI组件,因此不可能访问某些视图,这就是为什么处理程序会出手相救
Executor framework(https://stackoverflow.com/a/66567556/4770877)
ThreadPoolExecutor类,实现ExecutorService,对线程池进行精细控制(例如,核心池大小、最大池大小、保持活动时间等)
ScheduledThreadPoolExecutor-一个扩展ThreadPool Executor的类。它可以在给定延迟后或周期性地调度任务。
FutureTask
FutureTask执行异步处理,但是,如果结果尚未就绪或处理尚未完成,调用get()将阻塞线程
AsyncTaskLoaders
异步任务加载器,因为它们解决了异步任务固有的许多问题
IntentService
这是Android上长时间运行处理的事实选择,一个很好的例子是上传或下载大文件。即使用户退出应用程序,上传和下载也可能继续,您当然不想阻止用户在执行这些任务时使用应用程序。
JobScheduler
实际上,您必须使用JobInfo创建服务和创建作业。用于指定何时运行服务的条件的生成器。
RxJava
用于使用可观察序列组合异步和基于事件的程序的库。
Coroutines(Kotlin)
它的主要要点是,它使异步代码看起来非常像同步代码
阅读更多here、here和here,请点击此处。
nfzehxib9#
简单地说,
不要在UI线程中进行网络工作
例如,如果您执行HTTP请求,则这是一个网络操作。
解决方案:
1.您必须创建一个新线程
1.*或**使用AsyncTask class
方式:
把你所有的作品都放进去
1.新螺纹的
run()
方法1.或异步任务类的
doInBackground()
方法。但是:
当您从网络响应中获得某些内容并希望在视图中显示时(如在文本视图中显示响应消息),您需要返回UI线程。
如果不这样做,您将获得
ViewRootImpl$CalledFromWrongThreadException
。如何
1.使用AsyncTask时,从
onPostExecute()
方法更新视图1.或调用
runOnUiThread()
方法并更新run()
方法中的视图。o4tp2gmn10#
如果在主线程上执行的任何繁重任务占用太多时间,则会发生此异常。
为了避免这种情况,我们可以使用线程或执行器来处理
ljo96ir511#
只是为了明确地说明一些事情:
主线程基本上是UI线程。
所以说你不能在主线程中进行网络操作意味着你不能在UI线程中进行联网操作,这意味着你也不能在其他线程中的
*runOnUiThread(new Runnable() { ... }*
块中进行联网。(我只是花了很长时间才弄明白为什么我在主线程之外的其他地方出现了错误。这就是为什么“这个线程有帮助”,希望这个评论能帮助其他人。)
unhi4e5o12#
在你的活动中使用它
jchrr9hc13#
对我来说是这样的:
我测试应用程序的设备是4.1.2,这是SDK版本16!
确保目标版本与Android目标库相同。如果您不确定目标库是什么,请右键单击您的项目->*构建路径->Android,它应该是勾选的项目。
此外,正如其他人所提到的,包括访问互联网的正确权限:
zengzsys14#
这仅适用于针对HiveSDK或更高版本的应用程序。允许针对早期SDK版本的应用程序在其主事件循环线程上进行联网。
The error is the SDK warning!
pw9qyyiw15#
spektom的顶级答案非常完美。
如果您正在内联编写
AsyncTask
,而不是作为类进行扩展,并且除此之外,如果需要从AsyncTask
中获取响应,则可以使用以下get()
方法。(摘自他的例子。)