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

agxfikkp  于 2021-06-30  发布在  Java
关注(0)|答案(22)|浏览(282)

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

如何解决此问题?

gajydyqb

gajydyqb1#

公认的答案有一些明显的缺点。除非您真的知道自己在做什么,否则不建议使用asynctask进行联网。一些不利因素包括:
创建为非静态内部类的asynctask具有对封闭活动对象、其上下文以及该活动创建的整个视图层次结构的隐式引用。此引用防止在asynctask的后台工作完成之前对活动进行垃圾收集。如果用户的连接速度慢,和/或下载量大,这些短期内存泄漏可能会成为一个问题-例如,如果方向更改了几次(并且您没有取消正在执行的任务),或者用户导航离开活动。
asynctask有不同的执行特性,这取决于它执行的平台:在api级别4之前,asynctask在单个后台线程上串行执行;从api级别4到api级别10,asynctasks在最多128个线程的池中执行;从api级别11开始,asynctask在单个后台线程上串行执行(除非使用重载 executeOnExecutor 方法并提供一个替代执行器)。在ics上串行运行时工作良好的代码在姜饼上并发执行时可能会中断,比如说如果您无意中有执行依赖项的顺序。
如果您希望避免短期内存泄漏,在所有平台上都有定义良好的执行特性,并且有一个构建真正健壮的网络处理的基础,那么您可能需要考虑:
使用一个库,这对你来说是一个很好的工作-有一个很好的比较,在这个问题上的网络库,或
使用 Service 或者 IntentService 相反,也许是 PendingIntent 通过活动的 onActivityResult 方法。

intentservice方法

下侧:
代码和复杂性比 AsyncTask ,虽然没有你想象的那么多
将请求排队并在单个后台线程上运行它们。您可以通过替换 IntentService 有一个等价物 Service 实现,也许像这样。
嗯,我现在真的想不出其他人了
正面:
避免了短期内存泄漏问题
如果您的活动在网络运行过程中重新启动,它仍然可以通过其 onActivityResult 方法
比asynctask更好的平台来构建和重用健壮的网络代码。示例:如果您需要进行重要的上载,可以从 AsyncTask 在一个 Activity ,但如果用户上下文切换出应用程序以接听电话,系统可能会在上载完成之前终止应用程序。它不太可能用活动的 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 re-use, 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);
}

github项目包含一个完整的androidstudio/gradle项目。

falq053o

falq053o2#

对我来说是这样的:

<uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="10" />

我测试我的应用程序的设备是4.1.2,是sdk版本16!
确保目标版本与您的android目标库相同。如果您不确定您的目标库是什么,请右键单击您的项目->构建路径->android,它应该是勾选的目标库。
另外,如其他人所述,包括访问internet的正确权限:

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

vpfxa7rd3#

我用一种新方法解决了这个问题 Thread .

Thread thread = new Thread(new Runnable() {

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

thread.start();
lawou6xi

lawou6xi4#

您几乎应该总是在线程上运行网络操作,或者作为异步任务运行。
但是,如果您愿意接受后果,则可以删除此限制并覆盖默认行为。
添加:

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

StrictMode.setThreadPolicy(policy);

在你们班上,

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

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

后果:
你的应用程序将(在互联网连接不稳定的区域)变得无响应和锁定,用户感觉到缓慢,必须进行强制终止,你冒着活动管理器终止你的应用程序并告诉用户应用程序已停止的风险。
android提供了一些关于良好编程实践的好建议,以设计响应能力:http://developer.android.com/reference/android/os/networkonmainthreadexception.html

4sup72z8

4sup72z85#

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

ru9i0ody

ru9i0ody6#

在你的活动中使用这个

btnsub.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub

                    //Initialize soap request + add parameters
                    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME1);

                    //Use this to add parameters
                    request.addProperty("pincode", txtpincode.getText().toString());
                    request.addProperty("bg", bloodgroup.getSelectedItem().toString());

                    //Declare the version of the SOAP request
                    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);

                    envelope.setOutputSoapObject(request);
                    envelope.dotNet = true;

                    try {
                        HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

                        //this is the actual part that will call the webservice
                        androidHttpTransport.call(SOAP_ACTION1, envelope);

                        // Get the SoapResult from the envelope body.
                        SoapObject result = (SoapObject) envelope.getResponse();
                        Log.e("result data", "data" + result);
                        SoapObject root = (SoapObject) result.getProperty(0);
                        // SoapObject s_deals = (SoapObject) root.getProperty(0);
                        // SoapObject s_deals_1 = (SoapObject) s_deals.getProperty(0);
                        //

                        System.out.println("********Count : " + root.getPropertyCount());

                        value = new ArrayList<Detailinfo>();

                        for (int i = 0; i < root.getPropertyCount(); i++) {
                            SoapObject s_deals = (SoapObject) root.getProperty(i);
                            Detailinfo info = new Detailinfo();

                            info.setFirstName(s_deals.getProperty("Firstname").toString());
                            info.setLastName(s_deals.getProperty("Lastname").toString());
                            info.setDOB(s_deals.getProperty("DOB").toString());
                            info.setGender(s_deals.getProperty("Gender").toString());
                            info.setAddress(s_deals.getProperty("Address").toString());
                            info.setCity(s_deals.getProperty("City").toString());
                            info.setState(s_deals.getProperty("State").toString());
                            info.setPinecode(s_deals.getProperty("Pinecode").toString());
                            info.setMobile(s_deals.getProperty("Mobile").toString());
                            info.setEmail(s_deals.getProperty("Email").toString());
                            info.setBloodgroup(s_deals.getProperty("Bloodgroup").toString());
                            info.setAdddate(s_deals.getProperty("Adddate").toString());
                            info.setWaight(s_deals.getProperty("waight").toString());
                            value.add(info);
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    Intent intent = new Intent(getApplicationContext(), ComposeMail.class);
                    //intent.putParcelableArrayListExtra("valuesList", value);

                    startActivity(intent);
                }
            }).start();
        }
    });
kgsdhlau

kgsdhlau7#

spektom的最佳答案是完美的。
如果你在写 AsyncTask 内联的,而不是作为类扩展的,最重要的是,如果需要从 AsyncTask ,可以使用 get() 方法如下。

RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();

(从他的例子)

fd3cxomn

fd3cxomn8#

这仅适用于目标为honeycomb sdk或更高版本的应用程序。允许针对早期sdk版本的应用程序在其主事件循环线程上进行联网。
错误是sdk警告!

2sbarzqh

2sbarzqh9#

把你的代码放进去:

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
    }
}
huus2vyu

huus2vyu10#

这种情况发生在android3.0及更高版本中。从android3.0和更高版本开始,他们限制了在主线程/ui线程(活动中的on create和on resume方法产生的内容)中运行网络操作(访问互联网的函数)。
这是为了鼓励使用单独的线程进行网络操作。有关如何正确执行网络活动的更多详细信息,请参阅asynctask。

2lpgd968

2lpgd96811#

注意:asynctask在api级别30中已被弃用。
https://developer.android.com/reference/android/os/asynctask
当应用程序试图在其主线程上执行网络操作时,会引发此异常。在中运行代码 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"/>
lrpiutwd

lrpiutwd12#

基于网络的操作不能在主线程上运行。您需要在子线程上运行所有基于网络的任务或实现asynctask。
以下是如何在子线程中运行任务:

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

disho6za13#

不使用strictmode(仅在调试模式下)
不更改sdk版本
不要使用单独的螺纹
使用服务或异步任务
请参见堆栈 溢出问题:
android.os.networkonmainthreadexception从android发送电子邮件

kg7wmglp

kg7wmglp14#

只是为了清楚地说明一些事情:
主线程基本上是ui线程。
所以说不能在主线程中进行联网操作意味着不能在ui线程中进行联网操作,这意味着不能在 *runOnUiThread(new Runnable() { ... }* 在其他线程中阻塞。
)我只是有一个漫长的挠头时刻,试图找出为什么我得到了错误的地方以外,我的主要线程。这就是为什么;这条线有帮助;希望这条评论能对其他人有所帮助。)

f87krz0w

f87krz0w15#

关于这个问题已经有很多很好的答案了,但是从那以后已经有很多很好的图书馆问世了

相关问题