接受的答案有一些显著的缺点。不建议使用AsyncTask进行网络编程,除非你真的知道你在做什么。其中一些缺点包括:
- 作为非静态内部类创建的AsyncTask隐含引用了封闭Activity对象、它的上下文以及该Activity创建的整个View层次结构。此引用会阻止Activity被垃圾回收直到AsyncTask的后台工作完成。如果用户的连接速度很慢和/或下载很大,这些短期内存泄漏可能会成为问题 - 例如,如果方向改变了几次(而且你没有取消执行任务),或者用户从活动中导航离开。
- AsyncTask在不同平台上具有不同的执行特性:在API级别4之前,AsyncTask在单个后台线程上串行执行;从API级别4到API级别10,AsyncTask在多达128个线程的池中执行;从API级别11开始,AsyncTask在单个后台线程上串行执行(除非您使用重载的
executeOnExecutor
方法并提供另一个执行程序)。当ICS上串行运行良好的代码在Gingerbread上同时执行时,可能会出现问题,比如无意中有顺序执行依赖。
如果你想避免短期内存泄漏,在所有平台上具有明确定义的执行特性,并且有一个构建真正健壮的网络处理的基础,你可能会考虑:
- 使用一个很好为你做这个工作的库 - 在这个问题中有关于网络库的很好比较,或者
- 改用
Service
或IntentService
,也许使用PendingIntent
通过Activity的onActivityResult
方法返回结果。
IntentService方法
缺点:
- 比
AsyncTask
需要更多的代码和复杂性,但不像你想象的那么多 - 将请求排队并在一个单独的后台线程上运行。您可以通过使用等效的
Service
实现来替换IntentService
来轻松控制它,例如这个。 - 呃,我现在想不出其他的了
优点:
- 避免短期内存泄漏问题
- 如果您的活动在网络操作进行中重新启动,仍然可以通过其
onActivityResult
方法接收下载结果
- 比 AsyncTask 更好的平台来构建和重用强大的网络代码。例如:如果您需要进行重要的上传,您可以在Activity中使用AsyncTask完成,但是如果用户上下文切换到接听电话,则系统可能在上传完成之前杀死应用程序。具有活动Service的应用程序被杀死的可能性较小。
- 如果使用自己的并发版本的IntentService(如我上面链接的那个),则可以通过Executor控件并发级别。
实现摘要
您可以很容易地实现一个IntentService来在单个后台线程上执行下载。
步骤1:创建一个IntentService来执行下载。您可以通过Intent extras告诉它要下载什么,并传递一个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);
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);
}
}
}
第二步:在清单文件中注册服务:
<service
android:name=".DownloadIntentService"
android:exported="false"/>
步骤3:从活动中调用服务,传递一个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);
第四步:在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仓库,请点此查看。