在为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
如何解决此问题?
答案 0 :(得分:2423)
当应用程序尝试在其主线程上执行网络操作时,抛出此异常。在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"/>
答案 1 :(得分:626)
您几乎应该始终在线程上运行网络操作或作为异步任务。
但 可以删除此限制,如果您愿意接受后果,则覆盖默认行为。
添加:
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
答案 2 :(得分:391)
我使用新的Thread
解决了这个问题。
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
答案 3 :(得分:148)
接受的答案有一些重要的缺点。除非您确实知道您在做什么,否则不建议使用AsyncTask进行网络连接。一些不利方面包括:
executeOnExecutor
方法并提供备用执行程序)。当在Gingerbread上同时执行时,在ICS上串行运行的代码可能会中断,例如,如果您有无意的执行顺序依赖项。如果您想避免短期内存泄漏,在所有平台上都有明确定义的执行特性,并且有基础来构建非常强大的网络处理,您可能需要考虑:
Service
或IntentService
,也许使用PendingIntent
通过活动的onActivityResult
方法返回结果。向下侧:
AsyncTask
更多的代码和复杂性,尽管没有您想象的那么多IntentService
替换为等效的Service
实现来轻松控制此操作,可能与this one类似。向上侧:
onActivityResult
方法AsyncTask
中的Activity
进行,但如果用户上下文 - 切换出应用以拨打电话,系统< em>可能在上传完成之前终止该应用。杀死具有有效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:从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项目here。
答案 4 :(得分:139)
您无法在I/O上的UI线程上执行网络Honeycomb。从技术上讲,它在早期版本的Android上可能 ,但这是一个非常糟糕的主意,因为它会导致您的应用停止响应,并可能导致操作系统因为行为不当而导致您的应用被杀。您需要运行后台进程或使用AsyncTask在后台线程上执行网络事务。
在Android开发者网站上有一篇关于Painless Threading的文章,这是对此的一个很好的介绍,它将为您提供比这里实际提供的更好的答案深度。
答案 5 :(得分:71)
使用Service或AsyncTask
另请参阅Stack Overflow问题:
android.os.NetworkOnMainThreadException sending an email from Android
答案 6 :(得分:66)
在另一个线程上执行网络操作
例如:
new Thread(new Runnable(){
@Override
public void run() {
// Do network action in this function
}
}).start();
并将其添加到AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
答案 7 :(得分:56)
使用以下代码禁用严格模式:
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
不建议:使用AsyncTask
界面。
答案 8 :(得分:49)
无法在主线程上运行基于网络的操作。您需要在子线程上运行所有基于网络的任务或实现AsyncTask。
这是您在子线程中运行任务的方式:
new Thread(new Runnable(){
@Override
public void run() {
try {
// Your implementation goes here
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
答案 9 :(得分:46)
将您的代码放入:
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
}
}
答案 10 :(得分:43)
使用Android Annotations是一种选择。它允许您在后台线程中简单地运行任何方法:
// normal method
private void normal() {
doSomething(); // do something in background
}
@Background
protected void doSomething()
// run your networking code here
}
请注意,虽然它提供了简单性和可读性的好处,但它有其缺点。
答案 11 :(得分:42)
这在Android 3.0及更高版本中会发生。从Android 3.0及更高版本开始,他们限制使用网络操作(访问Internet的功能)在主线程/ UI线程中运行(从创建活动中生成的内容和活动中的恢复方法)。
这是为了鼓励使用单独的线程进行网络操作。有关如何以正确方式执行网络活动的更多详细信息,请参阅AsyncTask。
答案 12 :(得分:39)
错误是由于在主线程中执行长时间运行操作,您可以使用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
}
});
答案 13 :(得分:38)
您不应该在主线程(UI线程)上执行任何耗时的任务,例如任何网络操作,文件I / O或SQLite数据库操作。因此,对于这种操作,您应该创建一个工作线程,但问题是您不能直接从工作线程执行任何与UI相关的操作。为此,您必须使用Handler
并传递Message
。
为简化所有这些事项,Android提供了多种方式,例如AsyncTask
,AsyncTaskLoader
,CursorLoader
或IntentService
。因此,您可以根据自己的要求使用其中任何一种。
答案 14 :(得分:37)
顶部answer of spektom效果很好。
如果您正在编写AsyncTask
内联而不是作为一个类进行扩展,并且除此之外,如果需要从AsyncTask
中获取响应,则可以使用{ {1}}方法如下。
get()
(从他的例子开始。)
答案 15 :(得分:31)
仅针对定位Honeycomb SDK或更高版本的应用程序进行此操作。针对早期SDK版本的应用程序可以在其主要事件循环线程上进行联网。
答案 16 :(得分:26)
对我来说就是这样:
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="10" />
我正在测试我的应用程序的设备是4.1.2,它是SDK Version 16!
确保目标版本与Android目标库相同。如果您不确定目标库是什么,请右键单击您的项目 - &gt; 构建路径 - &gt; Android ,它应该是勾选的那个。
此外,正如其他人所提到的,包括访问互联网的正确权限:
<uses-permission android:name="android.permission.INTERNET"/>
答案 17 :(得分:23)
明确说明一些事情:
主线程基本上是UI线程。
所以说你不能在主线程中进行网络操作意味着你不能在UI线程中进行网络操作,这意味着你不能在*runOnUiThread(new Runnable() { ... }*
块中进行网络操作其他线程,或者。
(我只是有一个很长的头脑,试图弄清楚为什么我在主线程以外的地方得到了这个错误。这就是为什么;这个帖子有帮助;希望这个评论会帮助其他人。)
答案 18 :(得分:21)
如果执行任务执行太多时间,则会因主线程执行任何繁重任务而发生此异常。
为避免这种情况,我们可以使用线程或执行者
来处理Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
// You can perform your task here.
}
});
答案 19 :(得分:20)
在您的活动中使用此
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();
}
});
答案 20 :(得分:16)
这个问题已经有很多很好的答案了,但是自从这些答案发布以来,已经出现了许多优秀的图书馆。这是一种新手指南。
我将介绍几个用于执行网络操作的用例和一个解决方案,或者每个用例。
通常Json可以是XML或其他
让我们假设您正在编写一款应用程序,可让用户跟踪股票价格,利率和当前汇率。你会发现一个类似于这样的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)。
让我们说你正在建立一个情绪化的天气&#34;应用程序查找用户的GPS位置并检查该区域的当前温度并告诉他们心情。这种类型的应用程序不需要声明API端点;它只需要能够访问一个API端点。
对于此类访问,这是一个很棒的库。
请阅读msysmilu的好答案(https://stackoverflow.com/a/28559884/1024412)
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/
答案 21 :(得分:15)
简单来说,
不要在网络中进行网络工作
例如,如果您执行HTTP请求,那就是网络操作。
<强>解决方案:强>
<强>方式:强>
将所有作品放入
run()
新主题的方法doInBackground()
AsyncTask类的方法。<强>可是:强>
当您从网络响应中获得某些内容并希望在您的视图中显示它时(如在TextView中显示响应消息),您需要返回到UI 主题。
如果你不这样做,你将获得ViewRootImpl$CalledFromWrongThreadException
。
如何?
onPostExecute()
方法run()
方法中调用runOnUiThread()
方法和更新视图。答案 22 :(得分:10)
虽然上面有一个巨大的解决方案池,没有人提到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
}
});
答案 23 :(得分:9)
已经解释了新的Thread
和AsyncTask解决方案。
AsyncTask
应用于短期操作。普通Thread
不适合Android。
<强> HandlerThread 强>
用于启动具有looper的新线程的方便类。然后可以使用looper来创建处理程序类。请注意,仍然必须调用
start()
。
<强>处理程序:强>
Handler允许您发送和处理与线程的MessageQueue关联的Message和Runnable对象。每个Handler实例都与一个线程和该线程的消息队列相关联。当您创建一个新的Handler时,它被绑定到正在创建它的线程的线程/消息队列 - 从那时起,它将消息和runnables传递给该消息队列并在消息出来时执行它们队列中。
解决方案:
创建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);
使用这种方法的优点:
Thread/AsyncTask
是很昂贵的。 {@ 1}}将被销毁,并为下一次网络操作重新创建。但是,使用Thread/AsyncTask
和Handler
方法,您可以使用HandlerThread
向单个HandlerThread
提交许多网络操作(作为Runnable任务)。 答案 24 :(得分:9)
您可以将部分代码移动到另一个线程中以卸载main thread
并避免获取ANR,NetworkOnMainThreadException,IllegalStateException(例如,无法访问数据库)主线程,因为它可能会长时间锁定UI)。
根据具体情况,您应该选择一些方法
Java Thread或Android HandlerThread
Java线程只能一次性使用,并在执行其run方法后死掉。
HandlerThread是一个方便的类,用于启动具有looper的新线程。
AsyncTask 旨在成为围绕线程和处理程序的帮助程序类,并不构成通用的线程框架。理想情况下,AsyncTasks应该用于短操作(最多几秒钟。)如果需要保持线程长时间运行,强烈建议您使用java.util.concurrent包提供的各种API,例如执行者, ThreadPoolExecutor 和 FutureTask 。
线程池实施ThreadPoolExecutor,ScheduledThreadPoolExecutor ...
实现ExecutorService的ThreadPoolExecutor类,它可以对线程池进行精细控制(例如,核心池大小,最大池大小,保持活动时间等)。
ScheduledThreadPoolExecutor - 一个扩展ThreadPoolExecutor的类。它可以在给定的延迟之后或定期安排任务。
FutureTask执行异步处理,但是,如果结果尚未就绪或处理尚未完成,则调用get()将阻塞线程
AsyncTaskLoaders,因为它们解决了AsyncTask固有的许多问题
这是Android上长时间运行处理的事实选择,一个很好的例子就是上传或下载大文件。即使用户退出应用程序,上传和下载也可能继续,您肯定不希望阻止用户在执行这些任务时使用该应用程序。
实际上,您必须使用JobInfo.Builder创建服务并创建作业,该作业指定了何时运行服务的条件。
使用可观察序列编写异步和基于事件的程序的库。
Coroutines(Kotlin)
它的主要内容是,它使异步代码看起来非常像同步
在这里阅读更多:
8 ways to do asynchronous processing in Android and counting
The Evolution of Android Network Access
Using a Thread Pool in Android
Managing Threads and Custom Services
答案 25 :(得分:8)
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);
}
});
通过指定(Schedulers.io())
,RxAndroid将在另一个主题上运行getFavoriteMusicShows()
。
使用AndroidSchedulers.mainThread()
我们想在UI线程上观察这个Observable,即我们希望在UI线程上调用我们的onNext()
回调
答案 26 :(得分:7)
这很有效。刚刚让Dr.Luiji的答案变得更简单了。
new Thread() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
答案 27 :(得分:7)
在Android上,网络操作无法在主线程上运行。您可以使用Thread,AsyncTask(短期运行任务),Service(长时间运行的任务)来执行网络操作。
答案 28 :(得分:7)
从主(UI)线程访问网络资源会导致此异常。使用单独的线程或AsyncTask访问网络资源以避免此问题。
答案 29 :(得分:6)
您不得在Android上的UI线程上实施网络操作。你将不得不使用AsyncTask类来执行网络相关的操作,如发送API请求,从URL下载图像等,并使用AsyncTask的回调方法,你可以得到onPostExecute menthod的结果,你将在UI线程和你可以使用来自Web服务或类似内容的数据填充UI。
示例:假设您要从网址下载图片:https://www.samplewebsite.com/sampleimage.jpg
使用AsyncTask的解决方案: 分别是。
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权限。它会像魅力一样工作。 :)
答案 30 :(得分:6)
您可以使用KOTLIN
和ANKO
。
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/
doAsync {
var result = runLongTask()
uiThread {
toast(result)
}
}
简单,NetworkOnMainThread
在UI Thread
上运行后台作业时发生longTask job
,因此您需要做的一件事就是在后台运行ol {
padding-left: 0;
}
ol>li {
list-style-type: none;
counter-increment: list;
position: relative;
padding-left: 30px;
}
ol>li:before {
content: counter(list) ".";
position: absolute;
/* right: calc(100% - 25px); this is the same as "left: 5px" */
right: calc(100% - 20px); /* this will probably look better */
}
。您可以在Android应用中使用此方法和 Kotlin 与 Anko 进行此操作。
答案 31 :(得分:6)
还有另一种解决此问题的非常方便的方法 - 使用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()调度程序的支持。
答案 32 :(得分:5)
在Android上,网络操作无法在主线程上运行。您可以使用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
}
}
OR
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);
}
答案 33 :(得分:5)
主线程是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
答案 34 :(得分:4)
当应用程序尝试在其主线程上执行网络操作时,抛出此异常。 如果你的任务花了五秒钟以上,就需要关闭它。
在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
}
}
答案 35 :(得分:4)
您还可以使用以下代码使用严格模式解决此问题。它也是解决这个问题的另一种选择。
&array[3][0]
但最好的做法是使用AsyncTask。
答案 36 :(得分:4)
如果您使用的是Kotlin,则可以使用coroutine
fun doSomeNetworkStuff() {
GlobalScope.launch(Dispatchers.IO) {
// ...
}
}
答案 37 :(得分:4)
如何修复android.os.NetworkOnMainThreadException
什么是NetworkOnMainThreadException:
在Android中,我们必须在UI线程(主线程)上执行所有UI操作。如果我们在主线程上执行后台操作或某些网络操作,那么我们会冒这个异常的风险,并且应用程序不会响应。
如何修复它:
要避免此问题,您必须使用另一个线程进行后台操作或网络操作,例如使用asyncTask并使用某些库进行网络操作,如Volley,AsyncHttp等。
答案 38 :(得分:4)
在主线程上执行网络操作时抛出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();
}
答案 39 :(得分:3)
我们还可以使用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的链接。随意挖掘。
答案 40 :(得分:3)
我以一种简单的方式解决了这个问题...
我在oncreate
StrictMode.enableDefaults();
后添加并解决了这个问题。
或者
使用Service
或AsyncTask
来解决此问题
注意:
Do not change SDK version
Do not use a separate thread
更多信息,check this。
答案 41 :(得分:2)
由于Android正在处理单个线程,因此不应在主线程上执行任何网络操作。有各种方法可以避免这种情况。
使用以下方式执行网络操作
永远不要使用StrictMode.setThreadPolicy(策略),因为它会冻结您的用户界面并不是一个好主意。
答案 42 :(得分:2)
您无法在主线程或UI线程上调用网络。在Android上,如果你想呼叫网络,有两个选项 -
我个人更喜欢asynctask。有关详细信息,请参阅this link。
答案 43 :(得分:2)
我有一个类似的问题,我只是在您的活动的oncreate方法中使用了以下内容。
//allow strict mode
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
效果很好。
答案 44 :(得分:2)
使用AsycTask
Java
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");
科特林
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
}
}
致电科特林
MyNetworkTask().execute(url)
答案 45 :(得分:1)
我将返回值的网络访问函数转换为挂起函数,如下所示:
final = np.zeros(shape=(80, 10))
final[:,1:9] = np.random.randint(97, size=(80, 8))+1
final[:,9] = 99
然后修改我使用该函数的位置以适应此:
suspend fun isInternetReachable(): Boolean {
...
...
return result
}
答案 46 :(得分:1)
Android 不允许在主线程上运行长时间运行的操作。 因此,只需使用不同的线程并在需要时将结果发布到主线程即可。
new Thread(new Runnable() {
@Override
public void run() {
/*
// run operation here
*/
//after getting result
runOnUiThread(new Runnable() {
@Override
public void run() {
//post result to main thread
}
});
}
}).start();
答案 47 :(得分:1)
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的麻烦
答案 48 :(得分:1)
这些答案需要更新,以使用更现代的方式连接到Internet上的服务器并处理一般的异步任务,例如您可以找到在Google Drive API sample中使用任务的示例。在这种情况下,应使用相同的命令。我将使用OP的原始代码来演示这种方法。
首先,您需要定义一个非主线程执行程序,并且只需要执行一次:
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 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 访问该任务返回的值。 / p>
答案 49 :(得分:1)
理想情况下,AsyncTasks应该用于简短的操作(几秒钟 最多。)-developer-android
使用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线程的更多信息,请参见this
答案 50 :(得分:1)
如果您在kotlin和anko工作,则可以添加
doAsync {
method()
}
答案 51 :(得分:1)
你实际上可以开始一个新线程,我之前遇到过这个问题并通过这种方式解决了它。
答案 52 :(得分:1)
出现NetworkOnMainThread异常是因为您在默认线程(即UI线程)上调用了一些网络操作。 根据不允许的Android版本Android 3(Honeycomb),您应该在主线程之外调用网络操作。
您可以使用AsyncTask,IntentService或创建自己的线程并在run方法内部调用。 有关详情,请访问 Connecting to the Network 。
答案 53 :(得分:0)
您可以使用Kotlin-coroutines
class YoutActivity : AppCompatActivity, CoroutineScope {
override fun onCreate(...) {
launch { yourHeavyMethod() }
}
suspend fun yourHeavyMethod() {
async{ yourNetworkCall() }.await()
...
...
}
}
您可以遵循this指南。
答案 54 :(得分:0)
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)
答案 55 :(得分:0)
不同的选择:
使用普通的java runnable线程来处理网络任务,并可以使用runOnUIThread()来更新UI
如果您想在获得网络响应后更新用户界面,可以使用intentservice / async任务
答案 56 :(得分:0)
Android Jetpack引入了WorkManager
,它解决了Oreo中后台服务限制以及Lolipop下方的Alarm Manager和Lolipop上方的JobScheduler的使用的问题。
请使用WorkManager
在后台线程上运行任务,即使用户关闭了应用程序,该任务仍将继续运行。
答案 57 :(得分:0)
自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)
}
答案 58 :(得分:0)
这是一个使用 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();
答案 59 :(得分:0)
永远不要在UI线程上进行任何长时间的工作,长时间运行的工作可以是与服务器的通信,读/写文件等。这些任务应该在后台线程上,这就是为什么Service
,{{1} },AsyncTask
已创建。您可以禁用Threads
,以防止崩溃但从未建议过。
我建议你在调试模式下至少利用StrictMode
。使用下面的代码来获取在主线程上减慢App的任何问题的日志。
StrictMode
您可以设置不同的处罚 -
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
关于https://developer.android.com/reference/android/os/StrictMode.html
的内容非常多答案 60 :(得分:-4)
使用以下代码执行繁重的任务。
// 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();
}
}
}
答案 61 :(得分:-5)
在清单标记
之后,您必须在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);
}
答案 62 :(得分:-5)
Android不允许单独的进程进入主活动线程,并且HTTP连接是一个独立的线程。这就是你得到“ android.os.NetworkOnMainThreadException ”的原因。
在向用户显示webview之前,您可能需要检查实际的Internet连接,因为如果没有Internet,Web视图将向用户显示页面未找到错误,通常你不显示什么。
为了检查Internet可用性,可以使用ping命令,但是如果可以在Wi-Fi服务器上禁用Wi-Fi ping,则在这种情况下,您使用HTTP连接来检查请求的状态。
如果您在向用户显示webview之前检查自己的webview URL链接,这可能是正确的方法。在这种情况下,您可以使用Android的严格模式,但不允许所有策略,因为您不需要它。
您应该只为严格模式提供网络允许策略。只需将以下行添加到您的代码中,您就不会收到此错误。
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build();
StrictMode.setThreadPolicy(policy);