我如何解决android.os.NetworkOnMainThreadException?

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

我该如何解决这个问题?

我用简单的方法解决了这个问题

我在oncreate StrictMode.enableDefaults();后添加了StrictMode.enableDefaults(); 解决了这个问题

要么

使用ServiceAsyncTask来解决这个问题

注意:

 Do not change SDK version Do not use a separate thread 

更多, 请检查这一点 。

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()

  2. 通过使用AndroidSchedulers.mainThread()我们希望在UI线程上观察这个Observable,也就是我们想要在UI线程上调用onNext()回调onNext()

Thread和AsyncTask解决方案已经被解释了。

理想情况下AsyncTask应该用于短操作。 正常Thread是不适合Android的。

看看使用HandlerThread和Handler的备用解决方案

HandlerThread

方便的类开始一个新的线程,有一个活套。 然后可以使用循环来创建处理程序类。 请注意, start()仍然必须被调用。

处理器:

处理程序允许您发送和处理与线程的MessageQueue关联的消息和可运行对象。 每个Handler实例都与单个线程和该线程的消息队列相关联。 当你创建一个新的Handler的时候,它绑定到正在创建它的线程的线程/消息队列 – 从这一点开始,它将把消息和可运行的消息传递给消息队列,并在消息出来时执行它们队列。

解:

  1. 创建HandlerThread

  2. HandlerThread上调用start()

  3. 通过从HanlerThread获取Looper创建Handler HanlerThread

  4. 将您的网络操作相关的代码嵌入到Runnable对象中

  5. 将可运行任务提交给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将多个网络操作(如Runnable任务)提交给单个HandlerThread

如何解决android.os.NetworkOnMainThreadException

什么是NetworkOnMainThreadException:

在Android中,我们必须在UI线程(主线程)上执行所有的UI操作。 如果我们在主线程上执行后台操作或一些网络操作,那么我们有可能会发生这种异常,并且应用程序不会响应。

如何解决它:

为了避免这个问题,你必须使用另一个线程进行后台操作或者网络操作,比如使用asyncTask,并且使用一些库来进行像Volley,AsyncHttp等网络操作。

在主线程上执行网络操作时,会引发android.os.NetworkOnMainThreadException。 你最好在AsyncTask中去除这个异常。 这样写:

  new AsyncTask<Void,String,String>(){ @Override protected Void doInBackground(Void... params) { // Perform your network operation. // Get JSON or XML string from the server. // Store in a local variable (say response) and return. return response; } protected void onPostExecute(String results){ // Response returned by doInBackGround() will be received // by onPostExecute(String results). // Now manipulate your jason/xml String(results). } }.execute(); } 

2017答:

在这个问题上已经有很多很好的答案,但是自从这些答案发布以来,很多优秀的图书馆已经出现了。 这是作为一种新手指南。

我将介绍几种用于执行网络操作的用例,以及一个或两个解决方案。

重新HTTP

通常Json,可以是XML或其他的东西

完整的API访问

假设您正在编写一个应用程序,让用户跟踪股票价格,利率和货币汇率。 你会发现一个Json API,看起来像这样:

 http://api.example.com/stocks //ResponseWrapper<String> object containing a list of Srings with ticker symbols http://api.example.com/stocks/$symbol //Stock object http://api.example.com/stocks/$symbol/prices //PriceHistory<Stock> object http://api.example.com/currencies //ResponseWrapper<String> object containing a list of currency abbreviation http://api.example.com/currencies/$currency //Currency object http://api.example.com/currencies/$id1/values/$id2 //PriceHistory<Currency> object comparing the prices of the first currency (id1) to the second (id2) 

从广场改造

对于具有多个端点的API来说,这是一个很好的选择,并且允许您声明ReST端点,而不必像ion或Volley等其他库一样对它们进行单独编码。 (网站: http : //square.github.io/retrofit/ )

你如何使用财务API?

的build.gradle

将这些行添加到您的模块级别buid.gradle:

 implementation 'com.squareup.retrofit2:retrofit:2.3.0' //retrofit library, current as of September 21, 2017 implementation 'com.squareup.retrofit2:converter-gson:2.3.0' //gson serialization and deserialization support for retrofit, version must match retrofit version 

FinancesApi.java

 public interface FinancesApi { @GET("stocks") Call<ResponseWrapper<String>> listStocks(); @GET("stocks/{symbol}") Call<Stock> getStock(@Path("symbol")String tickerSymbol); @GET("stocks/{symbol}/prices") Call<PriceHistory<Stock>> getPriceHistory(@Path("symbol")String tickerSymbol); @GET("currencies") Call<ResponseWrapper<String>> listCurrencies(); @GET("currencies/{symbol}") Call<Currency> getCurrency(@Path("symbol")String currencySymbol); @GET("currencies/{symbol}/values/{compare_symbol}") Call<PriceHistory<Currency>> getComparativeHistory(@Path("symbol")String currency, @Path("compare_symbol")String currencyToPriceAgainst); } 

FinancesApiBuilder

 public class FinancesApiBuilder { public static FinancesApi build(String baseUrl){ return new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .build() .create(FinancesApi.class); } } 

FinancesFragment片段

 FinancesApi api = FinancesApiBuilder.build("http://api.example.com/"); //trailing '/' required for predictable behavior api.getStock("INTC").enqueue(new Callback<Stock>(){ @Override public void onResponse(Call<Stock> stockCall, Response<Stock> stockResponse){ Stock stock = stockCall.body(); //do something with the stock } @Override public void onResponse(Call<Stock> stockCall, Throwable t){ //something bad happened } } 

如果您的API需要一个API密钥或其他头像用户令牌等发送,Retrofit使这个很容易(请参阅这个真棒答案的详细信息: https : //stackoverflow.com/a/42899766/1024412 )。

关闭ReST API访问权限

假设您正在构建一个“情绪天气”应用程序,用于查找用户的GPS位置,并检查该区域的当前温度,并告诉他们情绪。 这种类型的应用程序不需要声明API端点; 它只需要能够访问一个API端点。

离子

这是一个伟大的图书馆这种类型的访问。

请阅读msysmilu伟大的答案( https://stackoverflow.com/a/28559884/1024412

通过HTTP加载图像

齐射

Volley也可以用于ReST API,但是由于需要更复杂的设置,我更喜欢使用Square的Retrofit( http://square.github.io/retrofit/

假设您正在构建社交网络应用程序,并想要加载朋友的个人资料照片。

的build.gradle

将这一行添加到你的模块级buid.gradle:

 implementation 'com.android.volley:volley:1.0.0' 

ImageFetch.java

Volley需要比Retrofit更多的设置。 你将需要创建一个像这样的类来设置一个RequestQueue,一个ImageLoader和一个ImageCache,但这不是太糟糕:

 public class ImageFetch { private static ImageLoader imageLoader = null; private static RequestQueue imageQueue = null; public static ImageLoader getImageLoader(Context ctx){ if(imageLoader == null){ if(imageQueue == null){ imageQueue = Volley.newRequestQueue(ctx.getApplicationContext()); } imageLoader = new ImageLoader(imageQueue, new ImageLoader.ImageCache() { Map<String, Bitmap> cache = new HashMap<String, Bitmap>(); @Override public Bitmap getBitmap(String url) { return cache.get(url); } @Override public void putBitmap(String url, Bitmap bitmap) { cache.put(url, bitmap); } }); } return imageLoader; } } 

user_view_dialog.xml

将以下内容添加到布局xml文件中以添加图像:

 <com.android.volley.toolbox.NetworkImageView android:id="@+id/profile_picture" android:layout_width="32dp" android:layout_height="32dp" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" app:srcCompat="@android:drawable/spinner_background"/> 

UserViewDialog.java

将以下代码添加到onCreate方法(Fragment,Activity)或构造函数(Dialog)中:

 NetworkImageView profilePicture = view.findViewById(R.id.profile_picture); profilePicture.setImageUrl("http://example.com/users/images/profile.jpg", ImageFetch.getImageLoader(getContext()); 

毕加索

Square的另一个优秀的图书馆。 请参阅网站的一些很好的例子: http : //square.github.io/picasso/

你实际上可以开始一个新的线程,我有这个问题,并通过这种方式解决它。

发生NetworkOnMainThread异常是因为您在默认线程(即UI线程)上调用了一些网络操作。 根据不允许的Android版本的Android 3 (Honeycomb),您应该在主线程之外调用网络操作。

您可以使用AsyncTask,IntentService或创建自己的线程并在run方法内调用。 有关更多信息,请访问连接到网络

由于Android正在使用单线程,因此您不应该在主线程上执行任何网络操作。 有各种方法可以避免这种情况。

使用以下方法执行网络操作

  • Asysnctask :对于不需要太多时间的小型操作。
  • 意向服务 :对于需要大量时间的网络操作。
  • 使用像VolleyRetrofit这样的自定义库来处理复杂的网络操作

切勿使用StrictMode.setThreadPolicy(policy) ,因为它会冻结您的用户界面,并不是一个好主意。

您不能在主线程或UI线程上调用网络。 在Android上,如果你想调用网络有两个选项 –

  1. 调用asynctask,它将运行一个后台线程来处理网络操作。
  2. 您可以创建自己的可运行线程来处理网络操作。

我个人更喜欢asynctask。 有关更多信息,请参阅此链接 。

你可以使用KOTLINANKO

KotlinAndroid的新官方语言更多关于它你可以在这里找到https://kotlinlang.org/docs/tutorials/kotlin-android.html

Anko在Android中支持Kotlin库,在这里有一些文档https://github.com/Kotlin/anko

这个解决方案真的很有用,只有几行代码写在@AntonioLeiva https://antonioleiva.com/anko-background-kotlin-android/

 doAsync { var result = runLongTask() uiThread { toast(result) } } 

尽管简单,但在UI Thread上运行后台作业时会发生NetworkOnMainThread ,因此您必须做的一件事就是在后台运行您的longTask job 。 您可以在Android应用程序中使用此方法和KotlinAnko进行操作。

永远不要在UI线程上做长时间的工作,长时间运行的工作可以和服务器通信,在文件上读/写等。这些任务应该在后台线程上,这就是为什么ServiceAsyncTaskThreads创建的原因。 您可以禁用StrictMode ,这将防止崩溃,但从来没有推荐。

我建议你在调试模式下至少占用StrictMode的优势。 使用下面的代码来获取任何问题的日志,这会减慢主线程上的应用程序。

 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectAll() .penaltyLog() .build()); 

你可以设置不同的处罚 –

 penaltyLog() // to print log penaltyDeath() // This will crash you App(so costly penalty) penaltyDialog() // Show alert when something went lazy on Main thread 

有很多关于https://developer.android.com/reference/android/os/StrictMode.html

主线程是UI线程,您不能在主线程中执行可能阻止用户交互的操作。您可以通过两种方式解决

强制像这样在mainthread中执行任务

 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); 

欲了解更多信息检查了这一点。 https://android-developers.googleblog.com/2009/05/painless-threading.html

使用下面的代码来执行繁重的任务。

 // Your package here import java.util.List; import org.apache.http.NameValuePair; import android.app.Activity; import android.app.ProgressDialog; import android.content.Context; import android.os.AsyncTask; import android.view.View.OnSystemUiVisibilityChangeListener; public class AsyncRequest extends AsyncTask<String, Integer, String> { Context context; ProgressDialog pDialog; // Three Constructors public AsyncRequest(Activity a, String m, List<NameValuePair> p) { context = a; method = m; parameters = p; } public AsyncRequest(Activity a) { this.caller = (OnAsyncRequestComplete) a; context = a; } public String doInBackground(String... urls) { //Perform your task here return result; } public void onPreExecute() { pDialog = new ProgressDialog(context); pDialog.setMessage("Please wait.."); pDialog.setCancelable(false); pDialog.show(); } public void onProgressUpdate(Integer... progress) { // You can implement some progressBar and update it in this record. // setProgressPercent(progress[0]); } public void onPostExecute(String response) { if (pDialog != null && pDialog.isShowing()) { pDialog.dismiss(); } // Get the result here } protected void onCancelled(String response) { if (pDialog != null && pDialog.isShowing()) { pDialog.dismiss(); } } } 

清单标签后,您必须简单地在manifest.xml中添加以下行

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

并在活动文件中绑定语句后添加以下代码

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

Android不允许单独的进程进入主活动线程,而HTTP连接在这里是一个独立的线程。 这就是你得到“ android.os.NetworkOnMainThreadException ”的原因。

在向用户显示webview之前,可能需要检查实际的Internet连接,因为如果没有Internet,Web视图将向用户显示页面未找到错误,通常您不会显示哪些内容。

为了检查互联网的可用性,可以使用ping命令,但是如果在Wi-Fi服务器上可以禁用Wi-Fi ping,那么在这种情况下,您可以使用HTTP连接来检查请求的状态。

如果您在向用户显示web视图之前检查自己的webview URL链接,这可能是正确的方法。 在这种情况下,您可以使用Android的严格模式,但不要允许所有策略,因为您不需要它。

你只应该给严格模式的网络允许策略。 只需将下面的代码添加到代码中,就不会出现这个错误。

 StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build(); StrictMode.setThreadPolicy(policy); 
Interesting Posts