如何修复 ‘android.os.NetworkOnMainThreadException’ 错误 技术背景 在 Android 应用开发中,当应用尝试在主线程上执行网络操作时,就会抛出 android.os.NetworkOnMainThreadException
异常。从 Android 3.0(Honeycomb,API 级别 11)及以上版本开始,系统限制了在主线程/UI 线程中进行网络操作,目的是鼓励开发者使用单独的线程来处理网络请求,以避免应用无响应或被系统杀死。
实现步骤 方法一:使用 AsyncTask(已在 API 级别 30 中弃用) 创建一个继承自 AsyncTask
的类: 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 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 ; } } protected void onPostExecute (RSSFeed feed) { } }
在 MainActivity.java
的 onCreate()
方法中执行任务: 1 new RetrieveFeedTask ().execute(urlToRssFeed);
在 AndroidManifest.xml
中添加网络权限: 1 <uses-permission android:name ="android.permission.INTERNET" />
方法二:使用新线程 1 2 3 4 5 6 7 8 9 10 11 12 13 Thread thread = new Thread (new Runnable () { @Override public void run () { try { } catch (Exception e) { e.printStackTrace(); } } }); thread.start();
同样,需要在 AndroidManifest.xml
中添加网络权限。
方法三:使用 IntentService 创建一个 IntentService
类: 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 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); 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) { reply.send(ERROR_CODE); } } catch (PendingIntent.CanceledException exc) { Log.i(TAG, "reply cancelled" , exc); } } }
在 AndroidManifest.xml
中注册服务: 1 2 3 <service android:name =".DownloadIntentService" android:exported ="false" />
在 Activity 中调用服务: 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);
在 onActivityResult
中处理结果: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @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 ; } } super .onActivityResult(requestCode, resultCode, data); }
方法四:使用 Android Annotations 1 2 3 4 5 6 7 8 9 private void normal () { doSomething(); }@Background protected void doSomething () { }
方法五:使用 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 () { } @Override public void onSuccess (int statusCode, Header[] headers, byte [] response) { } @Override public void onFailure (int statusCode, Header[] headers, byte [] errorResponse, Throwable e) { } @Override public void onRetry (int retryNo) { } });
核心代码 以下是使用新线程进行网络操作的核心代码示例:
1 2 3 4 5 6 7 8 9 10 new Thread (new Runnable () { @Override public void run () { try { } catch (Exception ex) { ex.printStackTrace(); } } }).start();
最佳实践 避免在主线程执行耗时任务,如网络操作、文件 I/O 或 SQLite 数据库操作。 根据不同场景选择合适的异步处理方式:对于简单的短期任务,可以使用 AsyncTask
,但要注意其在不同 API 级别上的执行特性。 对于需要长时间运行的任务或需要避免内存泄漏的情况,建议使用 Service
或 IntentService
。 确保在 AndroidManifest.xml
中添加网络权限: 1 <uses-permission android:name ="android.permission.INTERNET" />
常见问题 1. 使用 AsyncTask 时出现内存泄漏问题 原因 :非静态内部类形式创建的 AsyncTask
会隐式引用外部 Activity 对象,阻止 Activity 被垃圾回收。解决方法 :使用静态内部类或考虑使用其他异步处理方式,如 Service
或 IntentService
。2. 不同 API 级别上 AsyncTask 执行特性不同 原因 :AsyncTask
在不同 API 级别上的执行方式有所不同,可能导致代码在不同版本上表现不一致。解决方法 :使用 executeOnExecutor
方法并提供替代执行器,或者使用其他更稳定的异步处理方式。3. 仍然抛出 NetworkOnMainThreadException
异常 原因 :可能是在 runOnUiThread
块中执行了网络操作。解决方法 :确保所有网络操作都在单独的线程或异步任务中执行。