我在为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
我该如何解决这个问题?
当应用程序尝试在其主线程上执行网络操作时,将引发此异常。运行你的代码 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 manifest.xml文件中添加此权限:
<uses-permission android:name="android.permission.INTERNET"/>
后果:
您的应用程序将(在互联网连接不稳定的区域)变得无法响应并锁定,用户感知缓慢并且必须执行强制终止,并且您冒着活动管理器杀死您的应用并告诉用户该应用已停止的风险。
Android提供了一些关于良好编程实践的好技巧,以设计响应性:
http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html
我用新的方法解决了这个问题 Thread
。
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
你无法执行网络 I / O 在UI线程上 蜂窝。从技术上讲,它 是 可能在早期版本的Android上,但这是一个非常糟糕的主意,因为它会导致您的应用停止响应,并可能导致操作系统因为行为不当而导致您的应用被杀。您需要运行后台进程或使用AsyncTask在后台线程上执行网络事务。
有一篇关于的文章 无痛线程 在Android开发者网站上,这是一个很好的介绍,它将为您提供比这里实际提供的更好的答案深度。
接受的答案有一些重要的缺点。除非你这样做,否则不建议使用AsyncTask进行网络连接 真 知道你在做什么。一些不利方面包括:
- 作为非静态内部类创建的AsyncTask具有对封闭的Activity对象,其上下文以及该活动创建的整个View层次结构的隐式引用。此引用可防止在AsyncTask的后台工作完成之前对Activity进行垃圾回收。如果用户的连接速度很慢,和/或下载量很大,则这些短期内存泄漏可能会成为问题 - 例如,如果方向发生多次更改(并且您没有取消正在执行的任务),或者用户导航远离活动。
- AsyncTask具有不同的执行特性,具体取决于它执行的平台:API级别4之前AsyncTasks在单个后台线程上串行执行;从API级别4到API级别10,AsyncTasks在最多128个线程的池上执行;从API级别11开始,AsyncTask在单个后台线程上串行执行(除非您使用重载
executeOnExecutor
方法并提供另一种执行者)。当在Gingerbread上同时执行时,在ICS上串行运行时运行正常的代码可能会中断,例如,如果您有无意的执行顺序依赖项。
如果您想避免短期内存泄漏,在所有平台上都有明确定义的执行特性,并且有基础来构建非常强大的网络处理,您可能需要考虑:
- 使用一个为你做得很好的库 - 有一个很好的网络库比较 这个问题, 要么
- 用一个
Service
要么 IntentService
相反,也许是一个 PendingIntent
通过Activity返回结果 onActivityResult
方法。
IntentService方法
下行方面:
- 比代码和复杂性更多
AsyncTask
虽然没有你想象的那么多
- 将队列请求排队并在a上运行它们 单 背景线程。您可以通过替换轻松控制它
IntentService
与等价物 Service
实施,也许就像 这个。
- 嗯,实际上我现在想不出任何其他人
向上方面:
- 避免短期内存泄漏问题
- 如果您的活动在网络操作正在进行时重新启动,它仍然可以通过其接收下载结果
onActivityResult
方法
- 比AsyncTask更好的平台来构建和重用强大的网络代码。示例:如果您需要进行重要上传,可以从中进行
AsyncTask
在一个 Activity
,但如果用户上下文 - 切换出应用程序以接听电话,系统 可能 在上传完成之前杀死应用程序。它是 不太可能 杀死具有活动的应用程序 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对象,Service将使用该对象返回结果:
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);
}
一个github项目包含一个完整的Android-Studio / gradle项目 这里。
- 不要使用strictMode(仅在调试模式下)
- 不要更改SDK版本
- 不要使用单独的线程
使用Service或AsyncTask
另请参阅Stack Overflow问题:
android.os.NetworkOnMainThreadException从Android发送电子邮件
在另一个线程上执行网络操作
例如:
new Thread(new Runnable(){
@Override
public void run() {
// Do network action in this function
}
}).start();
并将其添加到AndroidManifest.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);
}
不建议这样做: 使用 AsyncTask
接口。
这两种方法的完整代码