问:

为 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

我该如何解决这个问题?

答1:

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com

注意:AsyncTask 在 API 级别 30 中已被弃用。 AsyncTask | Android Developers

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

class RetrieveFeedTask extends AsyncTask {

    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 文件:



所以这个在主线程上运行的网络操作只在 android 中而不是在标准 java 代码中存在问题(用 java 编写的代码但不适用于 android 应用程序)。??

这是一个很好的解决方案,它节省了我的时间!

由于 AsyncTask 已被弃用,最新的解决方案是什么?

答2:

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com

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

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

添加:

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

StrictMode.setThreadPolicy(policy);

在你的课堂上,

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



结果:

您的应用程序将(在互联网连接不稳定的区域)变得无响应并被锁定,用户认为速度很慢并且必须强制终止,并且您冒着活动管理器杀死您的应用程序并告诉用户应用程序已停止的风险。

Android 提供了一些关于响应性设计的良好编程实践的好技巧:NetworkOnMainThreadException | Android Developers

哇,谢谢你的解释,我现在明白了。我看到了一个应用程序,它已经在它的 java 类中实现了 ThreadPolicy,我有点困惑它在做什么。当网络低时,我看到了你所说的后果。

答3:

huntsbot.com – 程序员副业首选,一站式外包任务、远程工作、创意产品分享订阅平台。

我使用新的 Thread 解决了这个问题。

Thread thread = new Thread(new Runnable() {

    @Override
    public void run() {
        try  {
            //Your code goes here
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});

thread.start(); 

您将如何将参数传递给此?

如果您需要在请求后访问 UI,则需要在最后返回主线程,如 here 所述。

简单而且有效,非常感谢你:)

答4:

huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!

公认的答案有一些明显的缺点。除非您真的知道自己在做什么,否则不建议使用 AsyncTask 进行联网。一些缺点包括:

作为非静态内部类创建的 AsyncTask 具有对封闭 Activity 对象、其上下文以及由该 Activity 创建的整个 View 层次结构的隐式引用。此引用可防止在 AsyncTask 的后台工作完成之前对 Activity 进行垃圾收集。如果用户的连接速度很慢,和/或下载量很大,这些短期内存泄漏可能会成为一个问题——例如,如果方向改变了几次(并且你没有取消正在执行的任务),或者用户导航离开活动。

AsyncTask 具有不同的执行特性,具体取决于它执行的平台:在 API 级别 4 之前,AsyncTask 在单个后台线程上串行执行;从 API 级别 4 到 API 级别 10,AsyncTasks 在多达 128 个线程的池中执行;从 API 级别 11 开始,AsyncTask 在单个后台线程上串行执行(除非您使用重载的 executeOnExecutor 方法并提供替代执行器)。在 ICS 上串行运行时运行良好的代码在 Gingerbread 上并发执行时可能会中断,例如,如果您有无意的执行顺序依赖项。

如果你想避免短期内存泄漏,在所有平台上都有明确定义的执行特征,并且有一个基础来构建真正强大的网络处理,你可能需要考虑:

使用可以为您完成这项工作的库 - 在这个问题中有一个很好的网络库比较,或者使用服务或 IntentService 代替,可能使用 PendingIntent 通过活动的 onActivityResult 方法返回结果。

IntentService 方法

缺点:

比 AsyncTask 更多的代码和复杂性,虽然没有你想象的那么多

将请求排队并在单个后台线程上运行它们。您可以通过将 IntentService 替换为等效的 Service 实现来轻松控制这一点,也许就像这样。

嗯,我现在真的想不出其他人了

优点:

避免短期内存泄漏问题

如果您的活动在网络操作正在进行时重新启动,它仍然可以通过其 onActivityResult 方法接收下载结果

一个比 AsyncTask 更好的平台来构建和重用强大的网络代码。示例:如果您需要进行重要的上传,您可以从 Activity 中的 AsyncTask 进行,但如果用户上下文切换到应用程序之外接听电话,系统可能会在上传完成之前终止应用程序。杀死具有活动服务的应用程序的可能性较小。

如果您使用自己的 IntentService 并发版本(如我上面链接的那个),您可以通过 Executor 控制并发级别。

实施总结

您可以很容易地实现 IntentService 以在单个后台线程上执行下载。

第 1 步:创建一个 IntentService 以执行下载。您可以通过 Intent extras 告诉它要下载什么,并将 PendingIntent 传递给它以用于将结果返回给 Activity:

import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Intent;
import android.util.Log;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

public class DownloadIntentService extends IntentService {

    private static final String TAG = DownloadIntentService.class.getSimpleName();

    public static final String PENDING_RESULT_EXTRA = "pending_result";
    public static final String URL_EXTRA = "url";
    public static final String RSS_RESULT_EXTRA = "url";

    public static final int RESULT_CODE = 0;
    public static final int INVALID_URL_CODE = 1;
    public static final int ERROR_CODE = 2;

    private IllustrativeRSSParser parser;

    public DownloadIntentService() {
        super(TAG);

        // make one and reuse, in the case where more than one intent is queued
        parser = new IllustrativeRSSParser();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        PendingIntent reply = intent.getParcelableExtra(PENDING_RESULT_EXTRA);
        InputStream in = null;
        try {
            try {
                URL url = new URL(intent.getStringExtra(URL_EXTRA));
                IllustrativeRSS rss = parser.parse(in = url.openStream());

                Intent result = new Intent();
                result.putExtra(RSS_RESULT_EXTRA, rss);

                reply.send(this, RESULT_CODE, result);
            } catch (MalformedURLException exc) {
                reply.send(INVALID_URL_CODE);
            } catch (Exception exc) {
                // could do better by treating the different sax/xml exceptions individually
                reply.send(ERROR_CODE);
            }
        } catch (PendingIntent.CanceledException exc) {
            Log.i(TAG, "reply cancelled", exc);
        }
    }
}

第 2 步:在清单中注册服务:



第 3 步:从 Activity 调用服务,传递一个 PendingResult 对象,服务将使用该对象返回结果:

PendingIntent pendingResult = createPendingResult(
    RSS_DOWNLOAD_REQUEST_CODE, new Intent(), 0);
Intent intent = new Intent(getApplicationContext(), DownloadIntentService.class);
intent.putExtra(DownloadIntentService.URL_EXTRA, URL);
intent.putExtra(DownloadIntentService.PENDING_RESULT_EXTRA, pendingResult);
startService(intent);

第四步:在onActivityResult中处理结果:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == RSS_DOWNLOAD_REQUEST_CODE) {
        switch (resultCode) {
            case DownloadIntentService.INVALID_URL_CODE:
                handleInvalidURL();
                break;
            case DownloadIntentService.ERROR_CODE:
                handleError(data);
                break;
            case DownloadIntentService.RESULT_CODE:
                handleRSS(data);
                break;
        }
        handleRSS(data);
    }
    super.onActivityResult(requestCode, resultCode, data);
}

提供了一个 GitHub 项目,其中包含一个完整的 Android Studio/Gradle 项目here。

huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。

答5:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

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

Android 开发者网站上有一篇关于 Painless Threading 的文章对此进行了很好的介绍,它将为您提供比此处实际提供的更深入的答案。

答6:

huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!

这个问题有两种解决方案。

不要在主 UI 线程中使用网络调用。为此使用异步任务。在 setContentView(R.layout.activity_main);: if (android.os.Build.VERSION.SDK_INT > 9) { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll( )。建造(); StrictMode.setThreadPolicy(policy); }

并将下面的 import 语句放入您的 Java 文件中。

import android.os.StrictMode;

遵循您的第二个解决方案是一种不好的做法。异步是正确执行此操作的方法。如果您更改政策,您就像隐藏您的问题!

答7:

huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。

在另一个线程上执行网络操作。

例如:

new Thread(new Runnable(){
    @Override
    public void run() {
        // Do network action in this function
    }
}).start();

并将其添加到文件 AndroidManifest.xml:



答8:

huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求

不要使用 strictMode(仅在调试模式下) 不要更改 SDK 版本 不要使用单独的线程

使用 Service 或 AsyncTask

另请参阅堆栈溢出问题:

android.os.NetworkOnMainThreadException sending an email from Android

也许值得强调一点,如果你使用服务,你仍然需要创建一个单独的线程——服务回调在主线程上运行。另一方面,IntentService 在后台线程上运行其 onHandleIntent 方法。

您不应该将 AsyncTask 用于长时间运行的操作!指南指定最多 2 到 3 秒。

答9:

huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。

您可以使用以下代码禁用严格模式:

if (android.os.Build.VERSION.SDK_INT > 9) {
    StrictMode.ThreadPolicy policy = 
        new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
}

不建议这样做:使用 AsyncTask 界面。

Full code for both the methods

是的,会出现 ANR 错误。表示应用程序在 5 秒内没有响应。

这是一个非常糟糕的答案。您不应该更改线程的策略,而是编写更好的代码:不要在主线程上进行网络操作!

答10:

huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。

基于网络的操作不能在主线程上运行。您需要在子线程上运行所有基于网络的任务或实现 AsyncTask。

这是在子线程中运行任务的方式:

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation goes here
        } 
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();

Anonymous Runnable 不是最好的方法,因为它对封闭类有一个隐式引用,并防止它在线程完成之前被 GC 编辑!此外,该线程将以与主/美国线程相同的优先级运行,与生命周期方法和 UI 帧速率竞争!

答11:

打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!

将您的代码放入:

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();

或者:

class DemoTask extends AsyncTask {

    protected Void doInBackground(Void... arg0) {
        //Your implementation
    }

    protected void onPostExecute(Void result) {
        // TODO: do something with the feed
    }
}

原文链接:https://www.huntsbot.com/qa/YlLZ/how-can-i-fix-android-os-networkonmainthreadexception?lang=zh_CN&from=csdn

打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐