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

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

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

如何解决此问题?

up9lanfz

up9lanfz1#

您不应该在主线程(UI线程)上执行任何耗时的任务,如任何网络操作、文件I/O或SQLite数据库操作。因此,对于这种操作,您应该创建一个工作线程,但问题是您不能从工作线程直接执行任何与UI相关的操作。为此,您必须使用Handler并通过Message
为了简化所有这些,Android提供了多种方式,如AsyncTaskAsyncTaskLoaderCursorLoaderIntentService。因此,您可以根据您的需求使用这些。

qrjkbowd

qrjkbowd2#

RxAndroid是解决这个问题的另一个更好的替代方案,它使我们避免了创建线程,然后在Android UI线程上发布结果的麻烦。
我们只需要指定需要执行任务的线程,并且所有任务都在内部处理。

Observable<List<String>> musicShowsObservable = Observable.fromCallable(new Callable<List<String>>() {

  @Override
  public List<String> call() {
    return mRestClient.getFavoriteMusicShows();
  }

});

mMusicShowSubscription = musicShowsObservable
  .subscribeOn(Schedulers.io())
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Observer<List<String>>() {

    @Override
    public void onCompleted() { }

    @Override
    public void onError(Throwable e) { }

    @Override
    public void onNext(List<String> musicShows) {
        listMusicShows(musicShows);
    }
});

1.通过指定(Schedulers.io()),RxAndroid将在不同的线程上运行getFavoriteMusicShows()
1.通过使用AndroidSchedulers.mainThread(),我们希望在UI线程上观察到这个可观察的,即,我们希望我们的onNext()回调在UI线程中被调用。

1sbrub3j

1sbrub3j3#

主线程是UI线程,您不能在主线程中执行可能会阻止用户交互的操作。您可以通过两种方式解决此问题:
强制在主线程中执行任务,如下所示

StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(threadPolicy);

或者创建一个简单的处理程序,并根据需要更新主线程。

Runnable runnable;
Handler newHandler;

newHandler = new Handler();
runnable = new Runnable() {
    @Override
    public void run() {
         try {
            //update UI
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
};
newHandler.post(runnable);

并停止线程使用:

newHandler.removeCallbacks(runnable);

有关更多信息,请查看:Painless threading

mefy6pfw

mefy6pfw4#

尽管上面有一个巨大的解决方案池,但没有人提到com.koushikdutta.ion:https://github.com/koush/ion
它也是异步非常简单使用:

Ion.with(context)
.load("http://example.com/thing.json")
.asJsonObject()
.setCallback(new FutureCallback<JsonObject>() {
   @Override
    public void onCompleted(Exception e, JsonObject result) {
        // do stuff with the result or error
    }
});
tpxzln5u

tpxzln5u5#

这是可行的。我只是让路易吉博士的回答简单一点。

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

nbysray56#

新的ThreadAsyncTask解决方案已经解释过。
理想情况下,AsyncTask应用于短期操作。普通Thread不适用于Android。
查看使用HandlerThreadHandler的替代解决方案

手线

用于启动具有循环器的新线程的方便类。然后可以使用循环器创建处理程序类。请注意,仍然必须调用start()

处理程序:

处理程序允许您发送和处理与线程的MessageQueue关联的消息和可运行对象。每个处理程序示例都与一个线程和该线程的消息队列相关联。当您创建一个新的处理程序时,它将绑定到正在创建它的线程的线程/消息队列——从那时起,它将把消息和可运行项传递到该消息队列,并在它们从消息队列中出来时执行它们。
解决方案:
1.创建HandlerThread
1.在HandlerThread上呼叫start()
1.通过从HanlerThread获取Looper来创建Handler
1.在Runnable对象中嵌入您的网络操作相关代码
1.将Runnable任务提交给Handler
示例代码片段,地址为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);

使用这种方法的优点:
1.为每个网络操作创建新的Thread/AsyncTask是昂贵的。Thread/AsyncTask将被销毁并重新创建,用于下一次网络操作。但使用HandlerHandlerThread方法,您可以使用Handler将许多网络操作(作为可运行任务)提交给单个HandlerThread

w1jd8yoj

w1jd8yoj7#

Kotlin

如果您正在使用Kotlin,您可以使用*coroutine*:

fun doSomeNetworkStuff() {
    GlobalScope.launch(Dispatchers.IO) {
        // ...
    }
}
2admgd59

2admgd598#

您可以将代码的一部分移动到另一个线程中,以卸载main thread,并避免获取ANRNetworkOnMainThreadExceptionIllegalStateException(例如,无法访问主线程上的数据库,因为它可能会长时间锁定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)
它的主要要点是,它使异步代码看起来非常像同步代码
阅读更多hereherehere,请点击此处。

nfzehxib

nfzehxib9#

简单地说,

不要在UI线程中进行网络工作

例如,如果您执行HTTP请求,则这是一个网络操作。

解决方案:

1.您必须创建一个新线程
1.*或**使用AsyncTask class

方式:

把你所有的作品都放进去
1.新螺纹的run()方法
1.异步任务类的doInBackground()方法。

但是:

当您从网络响应中获得某些内容并希望在视图中显示时(如在文本视图中显示响应消息),您需要返回UI线程。
如果不这样做,您将获得ViewRootImpl$CalledFromWrongThreadException

如何

1.使用AsyncTask时,从onPostExecute()方法更新视图
1.调用runOnUiThread()方法并更新run()方法中的视图。

o4tp2gmn

o4tp2gmn10#

如果在主线程上执行的任何繁重任务占用太多时间,则会发生此异常。
为了避免这种情况,我们可以使用线程执行器来处理

Executors.newSingleThreadExecutor().submit(new Runnable() {
    @Override
    public void run() {
        // You can perform your task here.
    }
});
ljo96ir5

ljo96ir511#

只是为了明确地说明一些事情:
主线程基本上是UI线程。
所以说你不能在主线程中进行网络操作意味着你不能在UI线程中进行联网操作,这意味着你也不能在其他线程中的*runOnUiThread(new Runnable() { ... }*块中进行联网。
(我只是花了很长时间才弄明白为什么我在主线程之外的其他地方出现了错误。这就是为什么“这个线程有帮助”,希望这个评论能帮助其他人。)

unhi4e5o

unhi4e5o12#

在你的活动中使用它

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();
        }
    });
jchrr9hc

jchrr9hc13#

对我来说是这样的:

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

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

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

zengzsys14#

这仅适用于针对HiveSDK或更高版本的应用程序。允许针对早期SDK版本的应用程序在其主事件循环线程上进行联网。
The error is the SDK warning!

pw9qyyiw

pw9qyyiw15#

spektom的顶级答案非常完美。
如果您正在内联编写AsyncTask,而不是作为类进行扩展,并且除此之外,如果需要从AsyncTask中获取响应,则可以使用以下get()方法。

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

(摘自他的例子。)

相关问题