How do I fix android.os.NetworkOnMainThreadException?
我在为rssreader运行android项目时出错。
代码:
1 2 3 4 5 6 7 8 9 | 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(); |
显示以下错误:
1 | android.os.NetworkOnMainThreadException |
如何解决此问题?
当应用程序尝试在其主线程上执行网络操作时,会引发此异常。在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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 } } |
如何执行任务:
在
1 | new RetrieveFeedTask().execute(urlToRssFeed); |
别忘了把这个添加到
1 | <uses-permission android:name="android.permission.INTERNET"/> |
您应该几乎总是在一个线程上运行网络操作,或者作为一个异步任务运行。
但是,如果您愿意接受后果的话,可以取消这个限制并覆盖默认行为。
添加:
1 2 3 | StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); |
在你们班,
和
在android manifest.xml文件中添加此权限:
1 | <uses-permission android:name="android.permission.INTERNET"/> |
后果:
你的应用程序将(在不稳定的互联网连接区域)变得无响应和锁定,用户感觉缓慢,必须强制杀死,你冒着活动管理器杀死你的应用程序并告诉用户应用程序已经停止的风险。
Android在设计响应性的良好编程实践方面有一些很好的建议:http://developer.android.com/reference/android/os/networkonmainthreadexception.html
我用新的
1 2 3 4 5 6 7 8 9 10 11 12 13 |
公认的答案有一些明显的负面影响。不建议使用AsyncTask进行网络连接,除非您真正知道自己在做什么。下面的部分包括:
- 作为非静态内部类创建的AsyncTask对封闭活动对象、其上下文以及该活动创建的整个视图层次结构具有隐式引用。此引用防止在异步任务的后台工作完成之前对活动进行垃圾收集。如果用户的连接速度慢,并且/或者下载量大,这些短期内存泄漏可能会成为一个问题-例如,如果方向改变了几次(并且您不取消正在执行的任务),或者用户离开活动。
- 根据所执行的平台的不同,AsyncTask具有不同的执行特性:在API级别4之前,异步任务在单个后台线程上串行执行;从API级别4到API级别10,异步任务在最多128个线程的池上执行;从API级别11开始,异步任务在单个后台线程上串行执行(un少使用重载的
executeOnExecutor 方法并提供一个可选的执行器)。在IC上连续运行时工作良好的代码在姜饼上并发执行时可能会中断,例如,如果您无意中发现了执行依赖项的顺序。
如果您想要避免短期内存泄漏,在所有平台上都有定义良好的执行特性,并且有一个基础来构建真正强大的网络处理,那么您可能需要考虑:
使用方法
下边:
- 比
AsyncTask 更复杂的代码,尽管没有你想象的那么多 - 将请求排队并在单个后台线程上运行它们。通过用等效的
Service 实现替换IntentService ,您可以很容易地控制这一点,可能与此类似。 - 嗯,我现在真的想不出其他人了
上边:
- 避免短期内存泄漏问题
- 如果您的活动在网络运行期间重新启动,它仍然可以通过其
onActivityResult 方法接收下载结果。 - 比AsyncTask更好的平台来构建和重用健壮的网络代码。例如:如果你需要做一个重要的上传,你可以在一个
Activity 中从AsyncTask 进行上传,但是如果用户上下文切换到应用外接电话,系统可能会在上传完成之前终止应用。使用活动的Service 不太可能终止应用程序。 - 如果您使用自己的并发版本的
IntentService (如我上面链接的版本),则可以通过Executor 控制并发级别。
实施总结
您可以实现一个
第一步:创建一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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:在清单中注册服务:
1 2 3 | <service android:name=".DownloadIntentService" android:exported="false"/> |
步骤3:从活动调用服务,传递服务将用于返回结果的挂起的结果对象:
1 2 3 4 5 6 | 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中处理结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @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项目。
不能在蜂窝上的UI线程上执行网络I/O。从技术上讲,这在早期版本的Android上是可能的,但这是一个非常糟糕的主意,因为它会导致你的应用停止响应,并可能导致操作系统杀死你的应用程序,因为你的行为不好。您需要运行一个后台进程或使用AsyncTask在后台线程上执行网络事务。
安卓开发者网站上有一篇关于无痛线程的文章,这是一个很好的介绍,它将为您提供比这里实际提供的答案更深入的答案。
使用服务或异步任务
另请参见堆栈溢出问题:
android.os.networkonmainthreadexception从android发送电子邮件
在另一个线程上执行网络操作
For Example:
1 2 3 4 5 6 |
并将其添加到androidmanifest.xml
1 | <uses-permission android:name="android.permission.INTERNET"/> |
使用以下代码禁用严格模式:
1 2 3 4 5 | if (android.os.Build.VERSION.SDK_INT > 9) { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); } |
不建议这样做:使用
两种方法的完整代码
不能在主线程上运行基于网络的操作。您需要在子线程上运行所有基于网络的任务或实现AsyncTask。
这是在子线程中运行任务的方式:
1 2 3 4 5 6 7 8 9 10 11 |
将代码放入:
1 2 3 4 5 6 7 8 9 10 11 |
或:
1 2 3 4 5 6 7 8 9 10 |
使用Android注释是一种选择。它允许您简单地在后台线程中运行任何方法:
1 2 3 4 5 6 7 8 9 | // normal method private void normal() { doSomething(); // do something in background } @Background protected void doSomething() // run your networking code here } |
注意,尽管它提供了简单性和可读性的好处,但它也有其缺点。
这在Android 3.0及更高版本中发生。在android 3.0及更高版本中,他们限制使用网络操作(访问互联网的功能)在主线程/ui线程中运行(由活动中的on-create和on-resume方法生成)。
这是为了鼓励使用单独的线程进行网络操作。有关如何正确执行网络活动的详细信息,请参阅AsyncTask。
错误是由于在主线程中执行长时间运行的操作,您可以使用asyntask或thread轻松地纠正问题。您可以签出此库AsynchTtpClient以获得更好的处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 } }); |
您不应该在主线程(UI线程)上执行任何耗时的任务,例如任何网络操作、文件I/O或SQLite数据库操作。因此,对于这种操作,您应该创建一个工作线程,但问题是您不能直接从工作线程执行任何与UI相关的操作。为此,您必须使用
为了简化所有这些,android提供了各种各样的方法,比如
spektom的首要答案是完美的。
如果您是在内联编写
1 | RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get(); |
(从他的例子中。)
这只适用于针对蜂窝式SDK或更高版本的应用程序。针对早期SDK版本的应用程序可以在其主事件循环线程上进行网络连接。
错误是SDK警告!
对我来说是这样的:
1 2 3 | <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10" /> |
我测试我的应用程序的设备是4.1.2,它是SDK版本16!
确保目标版本与Android目标库相同。如果您不确定目标库是什么,右键单击您的项目->构建路径->Android,它应该是勾选的那个。
另外,正如其他人提到的,包括访问互联网的正确权限:
1 | <uses-permission android:name="android.permission.INTERNET"/> |
只是想明确地说:
主线程基本上是UI线程。
所以说您不能在主线程中进行连网操作意味着您不能在UI线程中进行连网操作,这意味着您也不能在其他线程内的
(我只是有一个很长的抓耳挠腮的时刻,试图弄明白为什么我会在主线程之外的其他地方得到这个错误。这就是原因;这个线索有帮助;希望这个评论能帮助其他人。)
如果执行任务花费的时间太长,则由于在主线程上执行的任何繁重任务而发生此异常。
为了避免这种情况,我们可以使用线程或执行器来处理它。
1 2 3 4 5 6 | Executors.newSingleThreadExecutor().submit(new Runnable() { @Override public void run() { // You can perform your task here. } }); |
在你的活动中使用这个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | 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(); } }); |
关于这个问题,已经有很多很好的答案了,但是自从这些答案被公布之后,已经有很多很棒的图书馆出现了。这是一种新手指南。
我将介绍几个用于执行网络操作的用例,以及每个用例的一两个解决方案。
搁置HTTP通常是JSON,可以是XML或其他
完全API访问假设您正在编写一个应用程序,它允许用户跟踪股票价格、利率和货币汇率。您会发现一个JSON API,看起来像这样:
1 2 3 4 5 6 | 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来说,这是一个很好的选择,它允许您声明其余的端点,而不必像使用其他库(如ion或volley)那样单独对它们进行编码。(网址:http://square.github.io/remodify/)
如何将其与财务API结合使用?
平地
将这些行添加到模块级buid.gradle:
1 2 | 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版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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); } |
财务顾问
1 2 3 4 5 6 7 8 9 | public class FinancesApiBuilder { public static FinancesApi build(String baseUrl){ return new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .build() .create(FinancesApi.class); } } |
财务片段片段
1 2 3 4 5 6 7 8 9 10 11 12 | 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密钥或其他头文件(如用户令牌等),那么通过改型可以很容易地做到这一点(有关详细信息,请参阅这个令人敬畏的答案:https://stackoverflow.com/a/42899766/1024412)。
一次性REST API访问假设你正在构建一个"情绪天气"应用程序,它可以查找用户的GPS位置,检查该区域的当前温度,并告诉他们情绪。这种类型的应用不需要声明API端点;它只需要能够访问一个API端点。
离子对于这种类型的访问,这是一个很好的库。
请阅读msysmilu的好答案(https://stackoverflow.com/a/28559884/1024412)
通过HTTP加载图像截击Volley也可以用于RESTAPI,但是由于需要更复杂的设置,我更喜欢使用上面的square的revention(http://square.github.io/revent/)
假设您正在构建一个社交网络应用程序,并希望加载朋友的个人资料图片。
平地
将此行添加到模块级buid.gradle:
1 | implementation 'com.android.volley:volley:1.0.0' |
爪哇
凌空抽射比改装需要更多的设置。您需要创建一个这样的类来设置一个请求队列、一个ImageLoader和一个ImageCache,但这并不太糟糕:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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; } } |
用户视图对话.xml
将以下内容添加到布局XML文件以添加图像:
1 2 3 4 5 6 7 | <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"/> |
用户视图对话框.java
将以下代码添加到onCreate方法(片段、活动)或构造函数(对话框)中:
1 2 | NetworkImageView profilePicture = view.findViewById(R.id.profile_picture); profilePicture.setImageUrl("http://example.com/users/images/profile.jpg", ImageFetch.getImageLoader(getContext()); |
毕加索
另一个来自广场的优秀图书馆。请访问该网站了解一些很好的例子:http://square.github.io/picasso/
简单来说,
不要在UI线程中进行网络工作
例如,如果您执行HTTP请求,这是一个网络操作。
解决方案:
方式:
把你所有的作品都放进去
但是:
当您从网络响应中得到一些东西并希望在视图中显示它时(如在textview中显示响应消息),您需要返回到UI线程。
如果你不这样做,你会得到
如何?
虽然上面有一个巨大的解决方案池,但没有人提到
它也是异步的,而且使用非常简单:
1 2 3 4 5 6 7 8 9 | 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 } }); |
新的
使用handlerThread和handler查看其他解决方案
手螺纹
Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that
start() must still be called.
汉德勒:
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
解决方案:
创建
在
从
在
向
示例代码段,地址为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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 +" "); } Log.d("Ravi","After IO call"); Log.d("Ravi",text.toString()); }catch( Exception err){ err.printStackTrace(); } } }; mainHandler.post(myRunnable); |
使用这种方法的优点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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); } }); |
通过指定
通过使用
您可以将代码的一部分移动到另一个线程中,以卸载
根据具体情况,您应该选择一些方法
Java线程或Android手线程
Java threads are one-time use only and die after executing its run method.
HandlerThread is a handy class for starting a new thread that has a looper.
异步任务
AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.
线程池实现threadpoolExecutor,scheduledthreadpoolExecutor…
ThreadPoolExecutor class that implements ExecutorService which gives fine control on the thread pool (Eg, core pool size, max pool size, keep alive time, etc.)
ScheduledThreadPoolExecutor - a class that extends ThreadPoolExecutor. It can schedule tasks after a given delay or periodically.
未来任务
FutureTask performs asynchronous processing, however, if the result is not ready yet or processing has not complete, calling get() will be block the thread
异步任务加载器
AsyncTaskLoaders as they solve a lot of problems that are inherent to AsyncTask
智能服务
This is the defacto choice for long running processing on Android, a good example would be to upload or download large files. The upload and download may continue even if the user exits the app and you certainly do not want to block the user from being able to use the app while these tasks are going on.
作业调度程序
Effectively, you have to create a Service and create a job using JobInfo.Builder that specifies your criteria for when to run the service.
RXJava
Library for composing asynchronous and event-based programs by using observable sequences.
共济会(Kotlin)
The main gist of it is, it makes asynchronous code looks so much like synchronous
阅读更多信息:
Android异步处理和计数的8种方法< BR>Android网络接入的发展在Android中使用线程池
管理线程和自定义服务
这是可行的。只是让Luiji医生的回答简单了一点。
1 2 3 4 5 6 7 8 9 10 |
从主(UI)线程访问网络资源会导致此异常。使用单独的线程或异步任务访问网络资源以避免此问题。
在Android上,网络操作不能在主线程上运行。您可以使用线程、异步任务(短运行任务)、服务(长运行任务)来执行网络操作。
解决这个问题还有另一个非常方便的方法——使用RxJava的并发功能。您可以在后台执行任何任务,并以非常方便的方式将结果发布到主线程,因此这些结果将被传递到处理链。
第一个经过验证的回答建议是使用asyntask。是的,这是一个解决方案,但现在已经过时了,因为周围有新的工具。
1 2 3 4 5 6 7 8 9 10 11 12 |
geturl方法提供了url地址,它将在主线程上执行。
makeCallParseResponse(..)-实际工作
processResponse(..)-将处理主线程上的结果。
异步执行的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | rx.Observable.defer(new Func0<rx.Observable<String>>() { @Override public rx.Observable<String> call() { return rx.Observable.just(getUrl()); } }) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.io()) .map(new Func1<String, Object>() { @Override public Object call(final String s) { return makeCallParseResponse(s); } }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Action1<Object>() { @Override public void call(Object o) { processResponse(o); } }, new Action1<Throwable>() { @Override public void call(Throwable throwable) { // Process error here, it will be posted on // the main thread } }); |
与AsyncTask相比,此方法允许任意次数切换调度程序(例如,在一个调度程序上提取数据,在另一个调度程序上处理这些数据(例如,scheduler.computation())。您还可以定义自己的调度程序。
要使用此库,请在build.gradle文件中包含以下行:
1 2 | compile 'io.reactivex:rxjava:1.1.5' compile 'io.reactivex:rxandroid:1.2.0' |
最后一个依赖项包括对.mainthread()调度程序的支持。
有一个优秀的电子书为RX Java。
您可以使用
Kotlin是Android的新官方语言,更多信息请参见此处。https://kotlinlang.org/docs/tutorials/kotlin-android.html网站
Anko支持Android中Kotlin的库,这里是一些文档网址:https://github.com/kotlin/anko
这个解决方案非常有用,只有@antonioleiva编写的几行代码https://antonioleiva.com/anko-background-kotlin-android/安卓/
1 2 3 4 5 6 | doAsync { var result = runLongTask() uiThread { toast(result) } } |
简单地说,
不允许在Android上的UI线程上实现网络操作。您将必须使用AsyncTask类执行与网络相关的操作,如发送API请求、从URL下载图像等,并且使用AsyncTask的回调方法,您可以得到OnPostExecute方法的结果,并且您将处于UI线程中,并且可以用来自Web服务的数据或类似的东西填充UI。
示例:假设要从URL下载图像:https://www.samplewebsite.com/sampleimage.jpg
使用异步任务的解决方案:分别是。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public class MyDownloader extends AsyncTask<String,Void,Bitmap> { @Override protected void onPreExecute() { // Show progress dialog super.onPreExecute(); } @Override protected void onPostExecute(Bitmap bitmap) { //Populate Ui super.onPostExecute(bitmap); } @Override protected Bitmap doInBackground(String... params) { // Open URL connection read bitmaps and return form here return result; } @Override protected void onProgressUpdate(Void... values) { // Show progress update super.onProgressUpdate(values); } } } |
注意:不要忘记在Android清单文件中添加Internet权限。它会很有魅力。:)
主线程是UI线程,您不能在主线程中执行可能会阻止用户交互的操作。你可以用两种方法来解决这个问题:
强制在主线程中执行这样的任务
1 2 | StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(threadPolicy); |
或者创建一个简单的处理程序并根据需要更新主线程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
要停止线程,请使用:
1 | newHandler.removeCallbacks(runnable); |
有关更多信息,请查看:无痛线程
您还可以使用下面的代码使用严格模式来解决此问题。这也是解决这个问题的另一种选择。
1 2 | StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); |
但最好的做法是使用AsyncTask。
如何修复android.os.networkonmainthreadexception
什么是网络维护例外:
在Android中,我们必须在UI线程(主线程)上执行所有的UI操作。如果我们在主线程上执行后台操作或某些网络操作,那么我们就有可能发生此异常,应用程序将不会响应。
如何修复:
为了避免这个问题,您必须使用另一个线程进行后台操作或网络操作,如使用AsyncTask,并使用一些库进行网络操作,如Volley、AsyncHTTP等。
我用一种简单的方法解决了这个问题…
我在
或
用
注:
1 2 | Do not change SDK version Do not use a separate thread |
有关更多信息,请查看此。
在主线程上执行网络操作时,会引发android.os.networkonmainthreadexception。最好在AsyncTask中执行此操作以删除此异常。请这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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(); } |
当应用程序尝试在其主线程上执行网络操作时,会引发此异常。如果你的任务超过5秒,就需要一个接近的部队。
在
1 2 3 4 5 6 7 8 9 10 11 |
我们还可以使用RXJava将网络操作移动到后台线程。这也相当简单。
1 2 3 4 5 | webService.doSomething(someData) .subscribeOn(Schedulers.newThread())-- This for background thread .observeOn(AndroidSchedulers.mainThread()) -- for callback on UI .subscribe(result -> resultText.setText("It worked!"), e -> handleError(e)); |
你可以用RxJava做更多的事情。这里有一些关于RxJava的链接。你可以随便挖。
Android中的RxJava异步任务
http://blog.stablekernel.com/replace-asynctask-asynctaskloader-rx-observable-rxjava-android-patterns/
由于android在一个线程上工作,所以您不应该在主线程上执行任何网络操作。有多种方法可以避免这种情况。
使用以下方法执行网络操作
- asysnctask:对于不需要太多时间的小型操作。
- 意向服务:用于需要大量时间的网络运营。
- 使用自定义库(如截击和翻新)进行处理复杂的网络操作
永远不要使用strictmode.setthreadpolicy(policy),因为它会冻结您的用户界面,一点也不好。
不能在主线程或UI线程上调用网络。在Android上,如果你想呼叫网络,有两种选择-
我个人更喜欢AsyncTask。有关更多信息,请参阅此链接。
你可以开始一个新的线程,我以前有过这个问题,用这种方法解决了它。
发生NetworkOnMainthread异常是因为您在默认线程(即UI线程)上调用了某些网络操作。根据不允许使用的Android版本Android 3(蜂窝),您应该在主线程之外调用网络操作。
您可以使用AsyncTask、IntentService,或者创建自己的线程并在run方法内部调用。有关详细信息,请访问连接到网络。
如果你在科特林和安科工作,你可以添加
1 2 3 | doAsync { method() } |
您可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 | class YoutActivity : AppCompatActivity, CoroutineScope { override fun onCreate(...) { launch { yourHeavyMethod() } } suspend fun yourHeavyMethod() { async{ yourNetworkCall() }.await() ... ... } } |
You can follow this guide.
不要在UI线程上做任何长时间运行的工作,长时间运行的工作可以是与服务器的通信、文件的读/写等。这些任务应该在后台线程上,这就是创建
我建议您至少在调试模式下利用
1 2 3 4 | StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectAll() .penaltyLog() .build()); |
你可以设定不同的惩罚-
1 2 3 | 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的内容
截至2018年,我建议在Kotlin中使用RxJava进行网络获取。下面是一个简单的例子。
1 2 3 4 5 6 7 8 9 10 | Single.fromCallable { // Your Network Fetching Code Network.fetchHttp(url) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { // What you need to do with your result on the view result -> view.updateScreen(result) } |
不同的选项:
使用普通的Java可运行线程来处理网络任务,并可以使用RunOnthRead()来更新UI
IntentService/Async任务可用于获取网络响应后要更新UI的情况。
使用下面的代码执行繁重的任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | // 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中的manifest标记后添加以下行
1 | <uses-permission android:name="android.permission.INTERNET"/> |
在活动文件中,在绑定语句后添加以下代码
1 2 3 4 | 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视图将向用户显示"找不到页面"错误,通常您不需要显示什么。
为了检查Internet的可用性,可以使用ping命令,但在Wi-Fi服务器上可以禁用Wi-Fi ping,因此在这种情况下,可以使用HTTP连接检查请求的状态。
如果您在向用户显示WebView之前检查自己的WebView URL链接,这可能是正确的方法。在这种情况下,你可以使用Android的严格模式,但不要允许所有的策略,因为你不需要它。
您应该只为严格模式提供网络允许策略。只需将下面的行添加到代码中,就不会出现此错误。
1 2 | StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build(); StrictMode.setThreadPolicy(policy); |