如何修复“android.os.NetworkOnMainThreadException”?

bz4sfanl  于 2022-09-16  发布在  Java
关注(0)|答案(29)|浏览(306)

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

如何解决此问题?

uoifb46i

uoifb46i16#

**注意:API级别30中不推荐使用异步任务。

AsyncTask | Android Developers**
当应用程序尝试在其主线程上执行网络操作时,会引发此异常。在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"/>
isr3a4wc

isr3a4wc17#

该错误是由于在主线程中执行长时间运行的操作导致的,您可以使用AsynTaskThread轻松纠正该问题。您可以 checkout 此库AsyncHTTPClient以获得更好的处理。

AsyncHttpClient client = new AsyncHttpClient();
client.get("http://www.google.com", new AsyncHttpResponseHandler() {

    @Override
    public void onStart() {
        // Called before a request is started
    }

    @Override
    public void onSuccess(int statusCode, Header[] headers, byte[] response) {
        // Called when response HTTP status is "200 OK"
    }

    @Override
    public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
        // Called when response HTTP status is "4XX" (for example, 401, 403, 404)
    }

    @Override
    public void onRetry(int retryNo) {
        // Called when request is retried
    }
});
f45qwnt8

f45qwnt818#

可以选择使用Android Annotations。它将允许您在后台线程中简单地运行任何方法:

// normal method
private void normal() {
    doSomething(); // do something in background
}

@Background
protected void doSomething() 
    // run your networking code here
}

注意,尽管它提供了简单易读的优点,但也有其缺点。

f0ofjuux

f0ofjuux19#

这在Android 3.0及以上版本中发生。从Android 3.0及以上版本开始,他们限制使用网络操作(访问互联网的功能)在主线程/UI线程中运行(从活动中的创建和恢复方法生成)。
这是为了鼓励使用单独的线程进行网络操作。有关如何正确执行网络活动的更多详细信息,请参阅AsyncTask

ktecyv1j

ktecyv1j20#

将代码放入:

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

或:

class DemoTask extends AsyncTask<Void, Void, Void> {

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

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

bjp0bcyl21#

基于网络的操作无法在主线程上运行。您需要在子线程上运行所有基于网络的任务,或者实现AsyncTask。
这是如何在子线程中运行任务:

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

pepwfjgg22#

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

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

fhg3lkii

fhg3lkii23#

1.不要使用strictMode(仅在调试模式下)
1.不要更改SDK版本
1.不要使用单独的螺纹
使用服务或异步任务
另请参阅堆栈溢出问题:

  • android.os。NetworkOnMainThreadException从Android发送电子邮件*
mfpqipee

mfpqipee24#

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

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

并将其添加到文件AndroidManifest.xml

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

7fhtutme25#

这个问题有两种解决方案。
1.不要在主UI线程中使用网络调用。为此使用异步任务。
1.在*setContentView(R.layout.activity_main);*之后将以下代码写入MainActivity文件:
如果(android.os.Build.VERSION.SDK_INT>9){StrictMode.ThreadPolicy policy=new-StrictMode.ThreadPolicy.Builder().permitAll().Build();StrictModel.SetThreadPolicys(policy);}
并将下面的import语句导入到Java文件中。

import android.os.StrictMode;
ibrsph3r

ibrsph3r26#

您不能在Hive上的UI线程上执行网络I/O。从技术上讲,这在早期版本的Android上是可能的,但这是一个非常糟糕的主意,因为它会导致你的应用停止响应,并可能导致操作系统因为你的应用表现不好而杀死你的应用。您需要运行后台进程或使用AsyncTask在后台线程上执行网络事务。
Android开发者网站上有一篇关于Painless Threading的文章,这是一篇很好的介绍,它将为您提供比这里实际提供的更深入的答案。

4uqofj5v

4uqofj5v27#

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

  • 作为非静态内部类创建的AsyncTask具有对封闭Activity对象、其上下文和该活动创建的整个View层次结构的隐式引用。此引用可防止在AsyncTask的后台工作完成之前对“活动”进行垃圾收集。如果用户的连接速度慢,并且/或者下载量大,这些短期内存泄漏可能会成为一个问题-例如,如果方向改变了几次(并且您没有取消正在执行的任务),或者用户导航离开“活动”。
  • AsyncTask具有不同的执行特性,具体取决于它执行的平台:在API级别4之前,异步任务在单个后台线程上连续执行;从API级别4到API级别10,AsyncTasks在最多128个线程的池中执行;从API级别11开始,AsyncTask在单个后台线程上连续执行(除非使用重载executeOnExecutor方法并提供替代执行器)。在ICS上串行运行时工作良好的代码在Gingerbread上并发执行时可能会中断,比如说,如果您无意中有执行依赖项的顺序。

如果您想避免短期内存泄漏,在所有平台上都有定义良好的执行特征,并且有构建真正健壮的网络处理的基础,那么您可能需要考虑:
1.使用一个为您做得很好的库-在this question中有一个很好的网络库比较,或者
1.使用e1d1e或IntentService代替,可能使用e1d 3d1e通过“活动”的onActivityResult方法返回结果。

意向服务方法

缺点:

  • AsyncTask更多的代码和复杂性,尽管没有您想象的那么多
  • 将请求排队并在一个单个后台线程上运行它们。您可以通过将IntentService替换为等效的Service实现来轻松控制这一点,比如this one
  • 嗯,实际上我现在想不出其他人了

正面:

  • 避免短期内存泄漏问题
  • 如果您的活动在网络运行过程中重新启动,它仍然可以通过onActivityResult方法接收下载结果
  • 比AsyncTask更好的平台,可以构建和重用健壮的网络代码。示例:如果您需要进行重要上传,您可以在Activity中从AsyncTask进行上传,但如果用户上下文切换出应用程序以进行电话呼叫,系统可能会在上传完成之前*终止应用程序。使用活动Service终止应用程序的可能性更小。
  • 如果您使用自己的并发版本IntentService(如我上面链接的版本),则可以通过Executor控制并发级别。

实施总结

您可以很容易地实现IntentService以在单个后台线程上执行下载。
步骤1:创建IntentService以执行下载。您可以告诉它通过Intent附加项下载什么,并将其传递给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:在清单中注册服务:

<service
        android:name=".DownloadIntentService"
        android:exported="false"/>

步骤3:从“活动”调用服务,传递服务将用于返回结果的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);

步骤4:在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);
}

可以使用包含完整工作的Android Studio/Gradle项目的GitHub项目here

8cdiaqws

8cdiaqws28#

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

Thread thread = new Thread(new Runnable() {

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

thread.start();
t30tvxxf

t30tvxxf29#

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

但是,如果你愿意接受后果的话,有可能取消这个限制,并覆盖默认行为。
添加:

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

StrictMode.setThreadPolicy(policy);

在你们班上,

  • 在Android清单中添加此权限。xml*文件:
<uses-permission android:name="android.permission.INTERNET"/>

后果:
您的应用程序将(在互联网连接不稳定的区域)变得无响应并锁定,用户会感觉到速度缓慢,必须强制停止,您可能会面临活动管理器杀死您的应用并告诉用户应用程序已停止的风险。
Android提供了一些关于良好编程实践的好提示,以设计响应能力:NetworkOnMainThreadException | Android Developers

相关问题