网站首页 > 编程文章 正文
技术背景
在 Android 开发中,从 Android 3.0(API 级别 11)及以上版本开始,系统不允许在主线程(也称为 UI 线程)中执行网络操作。这是因为网络操作通常是耗时的,如果在主线程中进行,会阻塞主线程,导致应用无响应(ANR,Application Not Responding),严重影响用户体验。当应用尝试在主线程执行网络操作时,就会抛出
android.os.NetworkOnMainThreadException 异常。
实现步骤
1. 使用 AsyncTask(已在 API 级别 30 中弃用)
AsyncTask 是 Android 提供的一个方便的类,用于在后台线程执行任务,并在主线程更新 UI。
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 的 onCreate 方法中执行任务:
new RetrieveFeedTask().execute(urlToRssFeed);
2. 使用新线程
创建一个新的线程来执行网络操作。
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
// 网络操作代码
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
3. 使用 IntentService
IntentService 是一个异步的、会自动停止的服务,适合执行长时间运行的后台任务。
步骤 1:创建 IntentService
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);
}
}
}
步骤 2:在清单文件中注册服务
<service
android:name=".DownloadIntentService"
android:exported="false"/>
步骤 3:从 Activity 调用服务
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;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
4. 使用 Retrofit
Retrofit 是一个优秀的网络请求库,用于处理 RESTful API。
步骤 1:添加依赖
在 build.gradle 中添加依赖:
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
步骤 2:定义 API 接口
public interface FinancesApi {
@GET("stocks")
Call<ResponseWrapper<String>> listStocks();
@GET("stocks/{symbol}")
Call<Stock> getStock(@Path("symbol")String tickerSymbol);
// 其他接口方法
}
步骤 3:构建 API 实例
public class FinancesApiBuilder {
public static FinancesApi build(String baseUrl){
return new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(FinancesApi.class);
}
}
步骤 4:调用 API
FinancesApi api = FinancesApiBuilder.build("http://api.example.com/");
api.getStock("INTC").enqueue(new Callback<Stock>(){
@Override
public void onResponse(Call<Stock> stockCall, Response<Stock> stockResponse){
Stock stock = stockCall.body();
// 处理响应
}
@Override
public void onFailure(Call<Stock> stockCall, Throwable t){
// 处理错误
}
});
最佳实践
- 选择合适的方法:根据任务的复杂度和需求选择合适的方法。对于简单的短期任务,可以使用 AsyncTask(如果在 API 级别 30 以下);对于长时间运行的任务,建议使用 IntentService 或 Retrofit 等库。
- 权限声明:在 AndroidManifest.xml 中添加网络权限:
<uses-permission android:name="android.permission.INTERNET"/>
- 异常处理:在网络操作中,要对可能出现的异常进行捕获和处理,如 IOException、MalformedURLException 等。
常见问题
1. 使用新线程时无法更新 UI
由于 Android 不允许在非主线程更新 UI,因此在新线程中执行网络操作后,如果需要更新 UI,可以使用 runOnUiThread 方法:
runOnUiThread(new Runnable() {
@Override
public void run() {
// 更新 UI 的代码
}
});
2. AsyncTask 导致内存泄漏
AsyncTask 如果作为非静态内部类使用,会持有外部 Activity 的引用,可能导致内存泄漏。可以将 AsyncTask 定义为静态内部类,并使用弱引用持有 Activity。
3. 网络请求超时或失败
网络请求可能会因为网络不稳定、服务器问题等原因超时或失败。可以设置合理的超时时间,并在请求失败时进行重试或提示用户。例如,使用 OkHttp 可以设置超时时间:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build();
猜你喜欢
- 2025-05-16 A Glimpse of Hollywood
- 2025-05-16 Moments in November丨Shanghai Daily & JING’AN News
- 2025-05-16 Rizhao's enterprise embraces resource recycling, eco-friendly growth
- 2025-05-16 《神偷3 死亡阴影》全解
你 发表评论:
欢迎- 最近发表
-
- 90岁老人科目三一把过火爆网络!考驾照、爱美甲……这群“90后”比年轻人潮
- DNF:遗忘之地任务流程,适用于新手起小号,装备修理白漂
- dnf手游:“首款流拍宝珠”,6位玩家眼睁睁看着500万泰拉丢失!
- dnf手游:小熊卡正式升级!可附魔到工会勋章,千万别卖!
- dnf手游:首张“涨抗魔”粉卡火了!以前3万没人要现在准备断货!
- dnf手游:团本币增加!3月26号打团有坑提前避开,浪费门票?
- dnf手游:涨价20倍!曾经没人要的,如今已是妥妥的T0材料!
- dnf手游:“官方鼓励回归”火了,白送玩家+20装备和王子卡等!
- dnf手游:无根之源千万别用!竞拍系统一定要抢否则错过崛起机会
- dnf手游:DNF手游各职业技能加点攻略、装备推荐!小白一看就会
- 标签列表
-
- spire.doc (70)
- instanceclient (62)
- solidworks (78)
- system.data.oracleclient (61)
- 按键小精灵源码提取 (66)
- pyqt5designer教程 (65)
- 联想刷bios工具 (66)
- c#源码 (64)
- graphics.h头文件 (62)
- mysqldump下载 (66)
- libmp3lame (60)
- maven3.3.9 (63)
- 二调符号库 (57)
- git.exe下载 (68)
- diskgenius_winpe (72)
- pythoncrc16 (57)
- solidworks宏文件下载 (59)
- qt帮助文档中文版 (73)
- satacontroller (66)
- hgcad (64)
- bootimg.exe (69)
- android-gif-drawable (62)
- axure9元件库免费下载 (57)
- libmysqlclient.so.18 (58)
- springbootdemo (64)
本文暂时没有评论,来添加一个吧(●'◡'●)