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

如何解决此问题?


当前回答

您可以将代码的一部分移动到另一个线程中以卸载主线程,并避免获得ANR、NetworkOnMainThreadException、IllegalStateException(例如,无法访问主线程上的数据库,因为它可能会长时间锁定UI)。

你应该根据情况选择一些方法

Java线程或Android HandlerThread:

Java线程只能一次性使用,并在执行其运行方法后死亡。HandlerThread是一个方便的类,用于启动具有looper的新线程。

AsyncTask(API级别30中已弃用)

AsyncTask被设计为围绕线程和处理程序的帮助类,不构成通用线程框架。AsyncTasks最好用于短操作(最多几秒钟)。如果需要让线程长时间运行,强烈建议您使用java.util.concurrent包提供的各种API,如Executor、ThreadPoolExecutor和FutureTask。

由于主线程独占UI组件,因此不可能访问某些视图,这就是为什么Handler会出手相助的原因

[执行器框架]

实现ExecutorService的ThreadPoolExecutor类,该类可以对线程池进行精细控制(例如,核心池大小、最大池大小、保活时间等)ScheduledThreadPoolExecutor-一个扩展ThreadPoolExecuto的类。它可以在给定延迟后或周期性地安排任务。

未来任务

FutureTask执行异步处理,但是,如果结果尚未就绪或处理尚未完成,则调用get()将阻塞线程

异步任务加载器

AsyncTaskLoader,因为它们解决了AsyncTask固有的许多问题

Intent服务

这是Android上长期运行处理的事实选择,一个很好的例子就是上传或下载大型文件。即使用户退出应用程序,上传和下载也可能会继续,并且您当然不想阻止用户在执行这些任务时使用应用程序。

作业调度程序

实际上,您必须使用JobInfo.Builder创建一个服务并创建一个作业,该作业指定了何时运行服务的条件。

RxJava语言

通过使用可观察序列来组合异步和基于事件的程序的库。

花冠(Kotlin)

它的主要要点是,它使异步代码看起来非常像同步代码

在这里、这里、这里和这里阅读更多信息。

其他回答

您可以将代码的一部分移动到另一个线程中以卸载主线程,并避免获得ANR、NetworkOnMainThreadException、IllegalStateException(例如,无法访问主线程上的数据库,因为它可能会长时间锁定UI)。

你应该根据情况选择一些方法

Java线程或Android HandlerThread:

Java线程只能一次性使用,并在执行其运行方法后死亡。HandlerThread是一个方便的类,用于启动具有looper的新线程。

AsyncTask(API级别30中已弃用)

AsyncTask被设计为围绕线程和处理程序的帮助类,不构成通用线程框架。AsyncTasks最好用于短操作(最多几秒钟)。如果需要让线程长时间运行,强烈建议您使用java.util.concurrent包提供的各种API,如Executor、ThreadPoolExecutor和FutureTask。

由于主线程独占UI组件,因此不可能访问某些视图,这就是为什么Handler会出手相助的原因

[执行器框架]

实现ExecutorService的ThreadPoolExecutor类,该类可以对线程池进行精细控制(例如,核心池大小、最大池大小、保活时间等)ScheduledThreadPoolExecutor-一个扩展ThreadPoolExecuto的类。它可以在给定延迟后或周期性地安排任务。

未来任务

FutureTask执行异步处理,但是,如果结果尚未就绪或处理尚未完成,则调用get()将阻塞线程

异步任务加载器

AsyncTaskLoader,因为它们解决了AsyncTask固有的许多问题

Intent服务

这是Android上长期运行处理的事实选择,一个很好的例子就是上传或下载大型文件。即使用户退出应用程序,上传和下载也可能会继续,并且您当然不想阻止用户在执行这些任务时使用应用程序。

作业调度程序

实际上,您必须使用JobInfo.Builder创建一个服务并创建一个作业,该作业指定了何时运行服务的条件。

RxJava语言

通过使用可观察序列来组合异步和基于事件的程序的库。

花冠(Kotlin)

它的主要要点是,它使异步代码看起来非常像同步代码

在这里、这里、这里和这里阅读更多信息。

已经介绍了新的线程和异步任务解决方案。

AsyncTask理想情况下应用于短操作。普通线程不适用于Android。

看看使用HandlerThread和Handler的替代解决方案

处理程序线程

用于启动带有弯针的新线程的便捷类。然后可以使用looper创建处理程序类。请注意,仍然必须调用start()。

处理程序:

处理程序允许您发送和处理与线程的MessageQueue关联的Message和Runnable对象。每个Handler实例都与一个线程和该线程的消息队列相关联。当您创建一个新的处理程序时,它被绑定到正在创建它的线程的线程/消息队列——从那时起,它将向该消息队列传递消息和可运行文件,并在它们从消息队列中出来时执行它们。

解决方案:

创建HandlerThread在HandlerThread上调用start()通过从HanlerThread获取Looper创建处理程序在Runnable对象中嵌入与网络操作相关的代码将可运行任务提交给处理程序

示例代码段,用于处理NetworkOnMainThreadException

HandlerThread handlerThread = new HandlerThread("URLConnection");
handlerThread.start();
handler mainHandler = new Handler(handlerThread.getLooper());

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Log.d("Ravi", "Before IO call");
            URL page = new URL("http://www.google.com");
            StringBuffer text = new StringBuffer();
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            conn.connect();
            InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
            BufferedReader buff = new BufferedReader(in);
            String line;
            while ( (line =  buff.readLine()) != null) {
                text.append(line + "\n");
            }
            Log.d("Ravi", "After IO call");
            Log.d("Ravi",text.toString());

        }catch( Exception err){
            err.printStackTrace();
        }
    }
};
mainHandler.post(myRunnable);

使用此方法的优点:

为每个网络操作创建新的线程/异步任务非常昂贵。线程/异步任务将被销毁并重新创建,以用于下一次网络操作。但使用Handler和HandlerThread方法,您可以通过使用Handler将许多网络操作(作为可运行任务)提交给单个HandlerThread。

Android Jetpack引入了WorkManager,它解决了Android 8.1(奥利奥)中后台服务限制的问题,并在Android 5.0(棒棒糖)以下使用Alarm Manager,在Lolipop以上使用JobScheduler。

请使用WorkManager在后台线程上运行任务,即使用户关闭应用程序,它也将继续运行。

这仅适用于针对蜂巢SDK或更高版本的应用程序。针对早期SDK版本的应用程序可以在其主事件循环线程上进行网络连接。

错误是SDK警告!

我解决了在Kotlin使用线程的问题。有很多使用Java的例子,所以我想在Kotlin中添加一个适合我的解决方案。

 Thread {
     println("NEW THREAD")
     callAPI() // add your own task
 }.start()

因此,正如许多其他人所解释的那样,您不能在调用时阻塞主线程,因此有必要创建一个新线程。