提问人:bejoy george 提问时间:6/14/2011 最后编辑:Peter Mortensenbejoy george 更新时间:7/5/2023 访问量:1488638
如何修复“android.os.NetworkOnMainThreadException”?
How can I fix '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
如何解决此问题?
答:
您无法在 Honeycomb 上的 UI 线程上执行网络 I/O。从技术上讲,这在早期版本的 Android 上是可能的,但这是一个非常糟糕的主意,因为它会导致您的应用程序停止响应,并可能导致操作系统因行为不佳而杀死您的应用程序。需要运行后台进程或使用 AsyncTask 在后台线程上执行网络事务。
Android 开发者网站上有一篇关于无痛线程的文章,这是一个很好的介绍,它将为您提供比此处实际提供的更好的答案深度。
注意:AsyncTask 在 API 级别 30 中已弃用。
异步任务 |Android 开发者
当应用程序尝试在其主线程上执行网络操作时,将引发此异常。在 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"/>
评论
您几乎总是应该在线程上运行网络操作或作为异步任务运行网络操作。
但是,如果您愿意接受后果,则可以删除此限制并覆盖默认行为。
加:
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
在你的课堂上,
和
在 Android 清单 .xml 文件中添加此权限:
<uses-permission android:name="android.permission.INTERNET"/>
后果:
您的应用将(在 Internet 连接不稳定的区域)变得无响应并锁定,用户会察觉到速度缓慢,必须强制杀伤,并且您冒着活动管理器终止您的应用并告诉用户应用已停止的风险。
Android 提供了一些关于良好编程实践的好提示,用于设计响应能力:NetworkOnMainThreadException |Android 开发者
评论
您可以使用以下代码禁用严格模式:
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
不建议这样做:使用界面。AsyncTask
评论
这个问题有两种解决方案。
不要在主 UI 线程中使用网络调用。为此使用异步任务。
将以下代码写入 MainActivity 文件的 setContentView(R.layout.activity_main);:
如果 (android.os.Build.VERSION.SDK_INT > 9) { StrictMode.ThreadPolicy 策略 = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(策略); }
并将下面的 import 语句添加到您的 Java 文件中。
import android.os.StrictMode;
评论
我使用新的.Thread
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
// Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
spektom 的最佳答案非常完美。
如果您正在编写内联而不是扩展为类,并且最重要的是,如果需要从 中获取响应,则可以使用以下方法。AsyncTask
AsyncTask
get()
RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();
(以他为例。
- 不要使用 strictMode(仅在调试模式下)
- 请勿更改 SDK 版本
- 不要使用单独的线程
使用 Service 或 AsyncTask
另请参阅 Stack Overflow 问题:
android.os.NetworkOnMainThreadException 从 Android 发送电子邮件
评论
这发生在 Android 3.0 及更高版本中。从 Android 3.0 及更高版本开始,它们限制了使用网络操作(访问互联网的函数)在主线程/界面线程(从 Activity 中的 on create 和 on resume 方法生成的操作)中运行。
这是为了鼓励使用单独的线程进行网络操作。有关如何以正确方式执行网络活动的更多详细信息,请参阅 AsyncTask。
对我来说是这样的:
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="10" />
我测试应用程序的设备是 4.1.2,即 SDK 版本 16!
确保目标版本与您的 Android 目标库相同。如果您不确定目标库是什么,请右键单击您的项目 -> 构建路径 -> Android,它应该是勾选的那个。
此外,正如其他人所提到的,包括访问 Internet 的正确权限:
<uses-permission android:name="android.permission.INTERNET"/>
评论
NetworkOnMainThreadException
在另一个线程上执行网络操作。
例如:
new Thread(new Runnable() {
@Override
public void run() {
// Do network action in this function
}
}).start();
并将其添加到文件AndroidManifest.xml中:
<uses-permission android:name="android.permission.INTERNET"/>
评论
不应在主线程(UI 线程)上执行任何耗时的任务,例如任何网络操作、文件 I/O 或 SQLite 数据库操作。所以对于这种操作,你应该创建一个工作线程,但问题是你不能直接从你的工作线程执行任何与UI相关的操作。为此,您必须使用并传递 .Handler
Message
为了简化所有这些操作,Android 提供了多种方式,例如 、 或 .因此,您可以根据需要使用其中任何一个。AsyncTask
AsyncTaskLoader
CursorLoader
IntentService
基于网络的操作不能在主线程上运行。您需要在子线程上运行所有基于网络的任务或实现 .AsyncTask
以下是在子线程中运行任务的方式:
new Thread(new Runnable() {
@Override
public void run() {
try {
// Your implementation goes here
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
评论
公认的答案有一些明显的缺点。除非您真的知道自己在做什么,否则不建议使用 AsyncTask 进行联网。一些缺点包括:
- 创建为非静态内部类的 AsyncTask 具有对封闭 Activity 对象、其上下文以及该活动创建的整个 View 层次结构的隐式引用。此引用可防止在 AsyncTask 的后台工作完成之前对 Activity 进行垃圾回收。如果用户的连接速度较慢和/或下载量很大,则这些短期内存泄漏可能会成为一个问题 - 例如,如果方向多次更改(并且您没有取消执行任务),或者用户离开活动。
- AsyncTask 具有不同的执行特征,具体取决于它执行的平台:在 API 级别 4 之前,AsyncTasks 在单个后台线程上串行执行;从 API 级别 4 到 API 级别 10,AsyncTasks 在最多包含 128 个线程的池上执行;从 API 级别 11 开始,AsyncTask 在单个后台线程上串行执行(除非使用重载方法并提供备用执行程序)。在 ICS 上串行运行时工作正常的代码在 Gingerbread 上同时执行时可能会中断,例如,如果您有无意中的执行顺序依赖项。
executeOnExecutor
如果要避免短期内存泄漏,在所有平台上具有明确定义的执行特征,并且具有构建真正强大的网络处理的基础,则可能需要考虑:
- 使用一个可以很好地为您完成这项工作的库 - 在这个问题中对网络库进行了很好的比较,或者
- 使用 a 或代替,也许使用 a 通过 Activity 的方法返回结果。
Service
IntentService
PendingIntent
onActivityResult
IntentService 方法
缺点:
- 代码和复杂性比 ,尽管没有您想象的那么多
AsyncTask
- 将对请求进行排队,并在单个后台线程上运行它们。您可以通过替换为等效的实现(可能像这个一样)来轻松控制它。
IntentService
Service
- 呃,我现在真的想不出其他的了
优点:
- 避免短期内存泄漏问题
- 如果您的活动在网络操作进行时重新启动,它仍然可以通过其方法接收下载结果
onActivityResult
- 一个比 AsyncTask 更好的平台,用于构建和重用强大的网络代码。示例:如果您需要执行重要上传,则可以从 中执行此操作,但如果用户在上下文中切换出应用以接听电话,则系统可能会在上传完成之前终止应用。它不太可能杀死具有活动 .
AsyncTask
Activity
Service
- 如果您使用自己的并发版本(如我上面链接的版本),则可以通过 .
IntentService
Executor
实施摘要
您可以非常轻松地实现在单个后台线程上执行下载。IntentService
第 1 步:创建一个以执行下载。你可以通过extras告诉它要下载什么,并传递给它一个用于将结果返回给: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 步:从 Activity 调用服务,传递一个 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 项目。
这仅适用于面向 Honeycomb SDK 或更高版本的应用程序。允许面向早期 SDK 版本的应用程序在其主事件循环线程上执行联网。
在“您的活动记录”中使用此功能
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();
}
});
使用 Android 注释是一种选择。它允许您简单地在后台线程中运行任何方法:
// normal method
private void normal() {
doSomething(); // do something in background
}
@Background
protected void doSomething()
// run your networking code here
}
请注意,尽管它提供了简单性和可读性的好处,但它也有其缺点。
评论
只是为了明确地说明一些事情:
主线程基本上是 UI 线程。
所以说你不能在主线程中做网络操作,意味着你不能在UI线程中做网络操作,这意味着你不能在*runOnUiThread(new Runnable() { ... }*
阻塞在其他线程中。
(我只是有很长一段时间的挠头时刻,试图弄清楚为什么我在主线程以外的地方遇到这个错误。这就是为什么;这个线程有帮助;希望这个评论能帮助到其他人。
该错误是由于在主线程中执行长时间运行的操作,您可以使用 AsynTask 或 Thread 轻松纠正问题。您可以签出此库 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
}
});
如果执行任务花费的时间过长,则由于在主线程上执行的任何繁重任务,都会发生此异常。
为了避免这种情况,我们可以使用线程或执行器来处理它
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
// You can perform your task here.
}
});
把你的代码放进去:
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
}
}
Android 不允许在主 Activity 线程中加入单独的进程,此处的 HTTP 连接是一个独立的线程。这就是您收到“android.os.NetworkOnMainThreadException”的原因。
在向用户显示 Web 视图之前,可能需要检查实际的 Internet 连接,因为如果没有 Internet,Web 视图将向用户显示“未找到页面”错误,这通常不会显示什么。
为了检查 Internet 可用性,可以使用 ping 命令,但在 Wi-Fi 的情况下,可以在 Wi-Fi 服务器上禁用 ping,因此在这种情况下,您可以使用 HTTP 连接来检查请求的状态。
如果您在向用户显示 Web 视图之前检查自己的 Web 视图 URL 链接,这可能是正确的方法。在这种情况下,您可以使用 Android 的严格模式,但不允许所有策略,因为您不需要它。
您只应为严格模式提供网络允许策略。只需将以下行添加到您的代码中,您就不会收到此错误。
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build();
StrictMode.setThreadPolicy(policy);
我用一种简单的方式解决了这个问题......
我添加并解决了这个问题。oncreate
StrictMode.enableDefaults();
或
使用或解决此问题Service
AsyncTask
注意:
Do not change SDK version
Do not use a separate thread
有关更多信息,请查看此内容。
评论
使用以下代码执行繁重的任务。
// 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();
}
}
}
这行得通。我只是让 Luiji 博士的回答更简单一些。
new Thread() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
虽然上面有一个巨大的解决方案池,但没有人提到:https://github.com/koush/ioncom.koushikdutta.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
}
});
您只需在文件 manifest.xml 中的 manifest 标记后面添加以下行
<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 上,网络操作不能在主线程上运行。您可以使用 Thread、AsyncTask(短期运行任务)、Service(长时间运行的任务)来执行网络操作。
简单来说,
不要在 UI 线程中执行网络工作
例如,如果执行 HTTP 请求,则为网络操作。
溶液:
- 您必须创建一个新线程
- 或使用 AsyncTask 类
道路:
把你所有的作品都放进去
- 新线程的方法
run()
- 或者 AsyncTask 类的方法。
doInBackground()
但:
当您从网络响应中获取某些内容并希望将其显示在视图上(例如在 TextView 中显示响应消息)时,您需要返回到 UI 线程。
如果你不这样做,你会得到.ViewRootImpl$CalledFromWrongThreadException
操作方法
- 使用 AsyncTask 时,从方法更新视图
onPostExecute()
- 或者调用
runOnUiThread()
方法并更新方法中的视图。run()
从主 (UI) 线程访问网络资源会导致此异常。使用单独的线程或 AsyncTask 来访问网络资源以避免此问题。
您实际上可以启动一个新线程。我以前遇到过这个问题,并用这种方式解决了它。
不允许在 Android 上的 UI 线程上实现网络操作。您必须使用 AsyncTask 类来执行与网络相关的操作,例如发送 API 请求、从 URL 下载图像等,并使用 的回调方法,您可以在 方法中获取结果,您将在 UI 线程中,您可以使用来自 Web 服务的数据填充 UI 或类似的东西。AsyncTask
onPostExecute
示例:假设您要从 URL 下载图像:https://www.samplewebsite.com/sampleimage.jpg
解决方案使用:分别。AsyncTask
<String, Void, Bitmap>
<Params, Progress, Result>
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 权限。它会像魅力一样工作。:)
评论
当应用程序尝试在其主线程上执行网络操作时,将引发此异常。 如果您的任务花费的时间超过 5 秒,则需要强制关闭。
在以下位置运行代码:AsyncTask
class RetrieveFeedTask extends AsyncTask<String, Void, Boolean> {
protected RSSFeed doInBackground(String... urls) {
// TODO: Connect
}
protected void onPostExecute(RSSFeed feed) {
// TODO: Check this.exception
// TODO: Do something with the feed
}
}
发生 NetworkOnMainThread 异常的原因是您在默认线程(即 UI 线程)上调用了某些网络操作。 根据不允许的Android版本Android 3(Honeycomb),您应该在主线程之外调用网络操作。
可以使用 AsyncTask、IntentService,也可以创建自己的线程并在 run 方法中调用。 有关更多信息,请访问连接到网络。
我们还可以使用 RxJava 将网络操作移动到后台线程。而且这也相当简单。
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 的一些链接。随意深入研究。
评论
还有另一种非常方便的方法可以解决这个问题——使用 RxJava 的并发功能。您可以在后台执行任何任务,并以非常方便的方式将结果发布到主线程,因此这些结果将被传递给处理链。
第一个经过验证的答案建议是使用 AsynTask。是的,这是一个解决方案,但现在已经过时了,因为周围有新的工具。
String getUrl() {
return "SomeUrl";
}
private Object makeCallParseResponse(String url) {
return null;
//
}
private void processResponse(Object o) {
}
getUrl 方法提供 URL 地址,它将在主线程上执行。
makeCallParseResponse(..)
- 做实际工作
processResponse(..)
- 将在主线程上处理结果。
异步执行的代码如下所示:
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 文件中包含以下行:
compile 'io.reactivex:rxjava:1.1.5'
compile 'io.reactivex:rxandroid:1.2.0'
最后一个依赖项包括对 .mainThread() 调度程序的支持。
如何修复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();
}
评论
AsyncTask
您还可以通过使用以下代码使用严格模式来解决此问题。这也是解决此问题的替代方法。
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
但最佳做法是使用 AsyncTask。
由于 Android 在单个线程上工作,因此您不应在主线程上执行任何网络操作。有多种方法可以避免这种情况。
使用以下方式执行网络操作
- Asysnctask:适用于不需要太多时间的小型操作。
- 意图服务:用于需要大量时间的网络操作。
- 使用自定义库(如 Volley 和 Retrofit)进行处理 复杂的网络操作
永远不要使用 StrictMode.setThreadPolicy(policy),因为它会冻结你的 UI,根本不是一个好主意。
不能在主线程或UI线程上调用network。在Android上,如果要调用网络,则有两种选择:
- 调用 asynctask,它将运行一个后台线程来处理网络操作。
- 您可以创建自己的可运行线程来处理网络操作。
就我个人而言,我更喜欢 asynctask。有关更多信息,您可以参考此链接。
新的和 AsyncTask 解决方案已经解释过了。Thread
AsyncTask
理想情况下,应用于短期操作。对于Android来说,Normal是不可取的。Thread
查看使用 HandlerThread 和 Handler 的替代解决方案
处理程序线程
方便的类,用于启动具有 looper 的新线程。然后,可以使用循环器创建处理程序类。请注意,仍必须调用。
start()
处理器:
Handler 允许您发送和处理与线程的 MessageQueue 关联的 Message 和 Runnable 对象。每个 Handler 实例都与单个线程和该线程的消息队列相关联。当您创建一个新的 Handler 时,它被绑定到创建它的线程的线程/消息队列 -- 从那时起,它将消息和可运行对象传递到该消息队列,并在它们从消息队列中出来时执行它们。
溶液:
创造
HandlerThread
拜访
start()
HandlerThread
通过获取
Handler
Looper
HanlerThread
在对象中嵌入与网络操作相关的代码
Runnable
将任务提交到
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);
使用此方法的优点:
- 为每个网络操作创建一个新的操作是昂贵的。将被销毁并重新创建,以用于下一次网络操作。但是,有了 和 方法,您可以使用 将许多网络操作(作为可运行任务)提交到 single。
Thread/AsyncTask
Thread/AsyncTask
Handler
HandlerThread
HandlerThread
Handler
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);
}
});
通过指定 ,RxAndroid 将在不同的线程上运行。
(Schedulers.io())
getFavoriteMusicShows()
通过使用,我们希望在 UI 线程上观察这个 Observable,即我们希望在 UI 线程上调用我们的回调。
AndroidSchedulers.mainThread()
onNext()
Kotlin 是 Android 的新官方语言。您可以在此处找到有关它的更多信息:适用于 Android 的 Kotlin。
Anko 是 Android 中 Kotlin 支持的库。一些文档位于 GitHub 页面上。
该解决方案非常有用,只有几行代码由 @AntonioLeiva 编写:使用 Anko 在 Android 中使用 Kotlin 运行后台任务 (KAD 09)。
doAsync {
var result = runLongTask()
uiThread {
toast(result)
}
}
虽然很简单,但当您在 UI 线程上运行后台作业时会发生,因此您必须做的一件事是在后台运行 longTask 作业。您可以在 Android 应用程序中使用此方法和 Kotlin 和 Anko 来执行此操作。NetworkOnMainThread
评论
主线程是 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);
有关更多信息,请查看以下内容: 无痛穿线
关于这个问题已经有很多很好的答案,但自从这些答案发布以来,已经出现了许多很棒的图书馆。这旨在作为一种新手指南。
我将介绍几个执行网络操作的用例,以及每个用例的一两个解决方案。
基于 HTTP 的 REST
通常是 JSON,但它可以是 XML 或其他内容。
完全 API 访问
假设您正在编写一个应用程序,让用户可以跟踪股票价格、利率和货币汇率。您会发现一个 JSON API ,如下所示:
http://api.example.com/stocks // ResponseWrapper<String> object containing a
// list of strings 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)
从Square进行改造
对于具有多个终端节点的 API 来说,这是一个很好的选择,它允许您声明 REST 终端节点,而不必像 Amazon Ion Java 或 Volley(网站:Retrofit)等其他库那样单独编写代码。
您如何将其与财务 API 一起使用?
文件 build.gradle
将以下行添加到模块级 build.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
文件财务Api.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);
}
类财务ApiBuilder
public class FinancesApiBuilder {
public static FinancesApi build(String baseUrl){
return new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(FinancesApi.class);
}
}
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 可以轻松实现此操作(有关详细信息,请参阅在 Retrofit 中添加标头参数的精彩答案)。
一次性 REST API 访问
假设您正在构建一个“情绪天气”应用程序,该应用程序查找用户的 GPS 位置并检查该区域的当前温度并告诉他们情绪。这种类型的应用不需要声明 API 终结点;它只需要能够访问一个 API 端点。
离子
对于这种类型的访问来说,这是一个很棒的库。
请阅读 msysmilu 对如何修复“android.os.NetworkOnMainThreadException”的精彩回答?
通过 HTTP 加载图像
截击
Volley 也可用于 REST API,但由于需要更复杂的设置,我更喜欢使用 Square 的 Retrofit,如上所述。
假设您正在构建一个社交网络应用程序,并希望加载朋友的个人资料图片。
文件 build.gradle
将以下行添加到模块级 build.gradle 文件中:
implementation 'com.android.volley:volley:1.0.0'
文件图像提取.java
凌空比改造需要更多的设置。你需要创建一个这样的类来设置一个 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的另一个优秀图书馆。请参阅网站以获取一些很好的例子。
截至 2018 年,我建议在 Kotlin 中使用 RxJava 进行网络获取。下面是一个简单的例子。
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)
}
评论
您可以将部分代码移动到另一个线程中,以卸载并避免获得 ANR、NetworkOnMainThreadException、IllegalStateException(例如,无法访问主线程上的数据库,因为它可能会长时间锁定 UI)。main thread
您应该根据情况选择一些方法
Java Thread 或 Android HandlerThread:
Java 线程只能一次性使用,并在执行其 run 方法后死亡。
HandlerThread 是一个方便的类,用于启动具有循环器的新线程。
AsyncTask(在 API 级别 30 中已弃用)
AsyncTask 被设计为围绕 Thread 和 Handler 的帮助程序类,并不构成通用线程框架。理想情况下,AsyncTasks 应用于短操作(最多几秒钟)。如果需要长时间保持线程运行,强烈建议使用 java.util.concurrent 包提供的各种 API,例如 Executor、ThreadPoolExecutor 和 FutureTask。
由于主线程垄断了 UI 组件,因此无法访问某些 View,这就是 Handler 来救援的原因
ThreadPoolExecutor 类,用于实现 ExecutorService,该类可以对线程池进行精细控制(例如,核心池大小、最大池大小、保持活动时间等)
ScheduledThreadPoolExecutor - 扩展 ThreadPoolExecutor 的类。它可以在给定的延迟后或定期安排任务。
FutureTask 执行异步处理,但是,如果结果尚未准备好或处理尚未完成,则调用 get() 将阻塞线程
AsyncTaskLoaders,因为它们解决了 AsyncTask 固有的许多问题
这是在 Android 上长时间运行处理的事实上的选择,一个很好的例子是上传或下载大文件。即使用户退出应用程序,上传和下载也可能继续,您当然不希望阻止用户在这些任务进行时使用应用程序。
实际上,您必须创建一个服务,并使用 JobInfo.Builder 创建一个作业,该作业指定何时运行该服务的条件。
用于使用可观察序列编写异步和基于事件的程序的库。
协程 (Kotlin)
它的主要要点是,它使异步代码看起来很像同步代码
不同的选项:
使用普通的 Java 可运行线程来处理网络任务,并且可以使用 runOnUIThread() 更新 UI
intentservice/ async 任务可用于在获得网络响应后要更新 UI 的情况
如果您在 Kotlin 和 Anko 中工作,您可以添加:
doAsync {
method()
}
您可以使用 Kotlin 协程:
class YoutActivity : AppCompatActivity, CoroutineScope {
override fun onCreate(...) {
launch { yourHeavyMethod() }
}
suspend fun yourHeavyMethod() {
with(Dispatchers.IO){ yourNetworkCall() }
...
...
}
}
您可以按照本指南进行操作。
Kotlin 版本
internal class RetrieveFeedTask : AsyncTask<String, Void, RSSFeed>() {
override fun doInBackground(vararg urls: String): RSSFeed? {
try {
// download
// prepare RSSFeeds
return RSSFeeds
} catch (e: Exception) {
//handle exception
return null
}
}
override fun onPostExecute(feed: RSSFeed) {
// TODO: check this.exception
// TODO: do something with the feed
}
}
调用示例,
RetrieveFeedTask().execute(url)
在后台线程中使用AsycTask
爪哇岛
class NetworkThread extends AsyncTask<String, Void, String> {
protected Void doInBackground(String... arg0) {
//Your implementation
}
protected void onPostExecute(String result) {
// TODO: do something with the feed
}
}
随时随地拨打电话
new NetworkThread().execute("Your URL here");
Kotlin
internal class MyNetworkTask : AsyncTask<String, Void, RSSFeed>() {
override fun doInBackground(vararg urls: String): RSSFeed? {
try {
// download
// prepare RSSFeeds
return RSSFeeds
} catch (e: Exception) {
//handle exception
return null
}
}
override fun onPostExecute(feed: RSSFeed) {
// TODO: check this.exception
// TODO: do something with the feed
}
}
调用 kotlin
MyNetworkTask().execute(url)
我也有类似的问题。我刚刚在您的活动的 oncreate 方法中使用了以下内容。
// Allow strict mode
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
而且效果很好。
需要注意的是,将其用于耗时超过 100 毫秒的网络请求将导致明显的 UI 冻结,并可能导致 ANR(应用程序无响应),因此请记住这一点。
评论
StrictMode
理想情况下,AsyncTasks 应用于短操作(最多几秒钟)。
使用是好的。您也可以考虑其他选项,例如,newCachedThreadPool
newSingleThreadExecutor
newFixedThreadPool
ExecutorService myExecutor = Executors.newCachedThreadPool();
myExecutor.execute(new Runnable() {
@Override
public void run() {
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);
}
});
ThreadPoolExecutor 是一个帮助程序类,可简化此过程。这 类管理一组线程的创建,设置它们的 优先级,并管理工作在这些线程之间的分配方式。 随着工作负载的增加或减少,类会启动或销毁 更多线程需要根据工作负载进行调整。
有关 Android 线程的更多信息,请参阅此处。
在 Android 上,网络操作不能在主线程上运行。您可以使用 、(短期运行任务)、(长时间运行的任务)来执行网络操作。 当应用程序尝试在其主线程上执行网络操作时引发。如果您的任务花费的时间超过 5 秒,则需要强制关闭。Thread
AsyncTask
Service
android.os.NetworkOnMainThreadException
在以下位置运行代码:AsyncTask
class FeedTask extends AsyncTask<String, Void, Boolean> {
protected RSSFeed doInBackground(String... urls) {
// TODO: Connect
}
protected void onPostExecute(RSSFeed feed) {
// TODO: Check this.exception
// TODO: Do something with the feed
}
}
或
new Thread(new Runnable(){
@Override
public void run() {
try {
// Your implementation
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
不建议这样做。
但出于调试目的,您也可以使用以下代码禁用严格模式:
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
这些答案需要更新,以使用更现代的方式连接到 Internet 上的服务器并处理一般的异步任务。
例如,您可以在 Google Drive API 示例中找到使用任务的示例。在这种情况下,也应使用相同的方法。我将使用 OP 的原始代码来演示此方法。
首先,您需要定义一个 off-main 线程执行器,并且只需执行一次:
private val mExecutor: Executor = Executors.newSingleThreadExecutor()
然后在该执行器中处理您的逻辑,该执行器将在主线程上运行
Tasks.call (mExecutor, Callable<String> {
val url = URL(urlToRssFeed)
val factory = SAXParserFactory.newInstance()
val parser = factory.newSAXParser()
val xmlreader = parser.getXMLReader()
val theRSSHandler = RssHandler()
xmlreader.setContentHandler(theRSSHandler)
val is = InputSource(url.openStream())
xmlreader.parse(is)
theRSSHandler.getFeed()
// Complete processing and return a String or other object.
// E.g., you could return Boolean indicating a success or failure.
return@Callable someResult
}).continueWith{
// it.result here is what your asynchronous task has returned
processResult(it.result)
}
continueWith 子句将在异步任务完成后执行,您将有权访问任务通过 it.result 返回的值。
Kotlin
如果您使用的是 Kotlin,则可以使用协程:
fun doSomeNetworkStuff() {
GlobalScope.launch(Dispatchers.IO) {
// ...
}
}
Google 在 Android 11 中弃用了 Android AsyncTask API。
即使您在 main 活动之外创建了一个线程类,只需在 main 中调用它,您也会得到相同的错误。这些调用必须位于可运行的线程中,但如果您需要一些异步代码在后台执行或之后在发布时执行一些代码,您可以查看 Kotlin 和 Java 的一些替代方案:
*https://stackoverflow.com/questions/58767733/android-asynctask-api-deprecating-in-android-11-what-are-the-alternatives*
特别适合我的是 mayank1513 对上述链接中可运行线程的 Java 8 实现的回答。代码如下:
new Thread(() -> {
// do background stuff here
runOnUiThread(() -> {
// OnPostExecute stuff here
});
}).start();
但是,您可以先在代码的某些部分定义线程,然后在其他位置启动它,如下所示:
线程定义
Thread thread = new Thread(() -> {
// do background stuff here
runOnUiThread(() -> {
// OnPostExecute stuff here
});
});
线程调用
thread.start();
我希望这能使某人免于看到被弃用的头痛.AsyncTask
下面是使用 、 和 的简单解决方案:OkHttp
Future
ExecutorService
Callable
final OkHttpClient httpClient = new OkHttpClient();
final ExecutorService executor = newFixedThreadPool(3);
final Request request = new Request.Builder().url("http://example.com").build();
Response response = executor.submit(new Callable<Response>() {
public Response call() throws IOException {
return httpClient.newCall(request).execute();
}
}).get();
评论
Android 不允许在主线程上运行长时间运行的操作。
因此,只需使用不同的线程并在需要时将结果发布到主线程即可。
new Thread(new Runnable() {
@Override
public void run() {
/*
// Run operation here
*/
// After getting the result
runOnUiThread(new Runnable() {
@Override
public void run() {
// Post the result to the main thread
}
});
}
}).start();
我将返回值的网络访问函数转换为挂起函数,如下所示:
suspend fun isInternetReachable(): Boolean {
...
...
return result
}
然后我修改了我使用该函数的位置以适应它:
...
Globalscope.async{
...
result = isInternetReachable()
...
}
...
Executors.newFixedThreadPool(3).execute(() -> {
//DO Task;
});
评论
我在 Kotlin 中使用解决了。有很多使用 Java 的例子,所以我想添加一个在 Kotlin 中对我有用的解决方案。Thread
Thread {
println("NEW THREAD")
callAPI() // add your own task
}.start()
因此,正如许多其他人所解释的那样,您不能通过调用来阻止主线程,因此有必要创建一个新线程。
评论