Android 中异步进行 HTTP 请求是否有通用的最佳实践?

42

我看过很多例子,它们似乎都用不同的方式解决这个问题。基本上我只想要最简单的方法来发送请求,而且不会锁定主线程并且可以取消。

同时我们还有(至少)两个HTTP库可供选择,java.net.* (如HttpURLConnection)和org.apache.http.*。

是否有关于最佳实践的共识?


有人之前使用过这个库吗?它看起来不错,但我不确定它如何处理失败的请求(甚至不确定它是否有效!) - Robert
请查看以下链接以获取更简单的方法:http://masl.cis.gvsu.edu/2010/04/05/android-code-sample-asynchronous-http-connections/ - ANUP
你能分享一下你的 HttpTask 类吗? - the_machinist_
最佳实践是对于异步操作(HTTP请求或其他操作),使用AsyncTask。 - jeremy
4个回答

35

Android 1.5 SDK引入了一个新类,AsyncTask,旨在使在后台线程上运行任务并将结果传递到UI线程变得更加简单。在Android Developers Blog中给出的示例说明了如何使用它的基本思想:

public void onClick(View v) {
   new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask {
   protected Bitmap doInBackground(String... urls) {
      return loadImageFromNetwork(urls[0]);
   }

   protected void onPostExecute(Bitmap result) {
      mImageView.setImageBitmap(result);
   }
}

doInBackgroundThread 方法在一个独立的线程上被调用(由线程池的 ExecutorService 管理),并将结果传递给在 UI 线程上运行的 onPostExecute 方法。您可以调用 AsyncTask 子类的 cancel(boolean mayInterruptIfRunning) 方法来取消正在运行的任务。

至于使用java.netorg.apache.http库进行网络访问,这取决于您。我发现当简单地尝试发出一个GET请求并读取结果时,java.net库非常好用。但是,org.apache.http库允许您几乎可以对HTTP做任何事情,但它们可能更难使用,并且我发现它们在 Android 上性能不如简单的GET请求。


谢谢。我实际上已经找到了AsyncTask(在1.5中)和UserTask(适用于1.1),并编写了一个通用的HttpTask。现在唯一剩下的事情就是弄清楚如何在请求仍在进行时处理屏幕旋转。 - Jeremy Logan
4
对于阅读此答案的新用户:请注意。这是一个快速而简单的修复复杂问题的方法。正如Snicolas在他的回答中指出的那样,它存在缺陷和漏洞。另外,它在处理多个并发任务时也不一致(http://developer.android.com/reference/android/os/AsyncTask.html)。从Honeycomb开始,所有任务都将在单个线程上执行——这意味着除非您明确要求`THREAD_POOL_EXECUTOR`并实现所需的并发管道,否则您将无法同时进行多个HTTP调用。 - Nilzor

12

事实上,AsyncTask的设计存在一些缺陷,使其在网络应用方面并不十分实用。一个简单的例子是:只要你旋转设备,你就会失去AsyncTask和Activity之间的链接。

请参考这个线程:您将看到AsyncTask还很容易造成内存泄漏。

AsyncTask文档已经很明确了:AsyncTask只适用于短时间的任务, 很明显,网络并不属于这一类别。

因此,我强烈建议您查看RoboSpice。这个库专门为异步网络设计,非常强大且实用。 如果您只有几秒钟的时间来说服您,请查看这个信息图表

RoboSpice还在商店里提供一个演示应用程序:RoboSpice Motivations,它将深入解释关于Android上异步网络的一切。


1
我认为你对Google打算让AsyncTask用于什么有误解。他们将“短暂的任务”定义为“最多几秒钟”(http://developer.android.com/reference/android/os/AsyncTask.html),并且确实提供了一个代码示例,其中执行了从URI下载文件的操作。 - Nilzor
你尝试在下载时旋转设备了吗?确实存在一些问题,但也有一些解决方法。问题在于它们往往非常复杂,难以正确实现,而且代码不可重用。例如,我建议你去商店看看RoboSpice的动机。 - Snicolas
1
(一本正经地说)我仍然认为你对Google的宣传有误。这很可怕,因为我也认为AsyncTask不适合HTTP请求。在按照你的建议测试了旋转设备和Motivations应用程序之后,我更加坚信这一点。编辑:正是你的句子“AsyncTask文档已经明确说明了这一点”触发了我的评论,因为我不同意。 - Nilzor
顺便问一下,你有没有看过Android异步Http客户端(http://loopj.com/android-async-http/),你能说一下它与Robospice相比如何吗? - Nilzor
2
不,但是可以进行比较。顺便说一句,评论开始变得难以阅读。如果我们都认为AsyncTasks不适合网络编程,那么我们应该直接说出来。 - Snicolas

1

如果使用一个引用了静态保留Fragment(没有UI)的AsyncTask,放在Activity或另一个Fragment中,这会怎样呢?没有内存泄漏,在单独的线程中进行优雅的异步操作,不会丢失对象引用。

我认为对于http请求来说这应该没问题,但对于文件上传/下载就不是很好。如果你仔细阅读,有一句话:

如果您需要长时间运行线程,强烈建议使用java.util.concurrent包提供的各种API,如Executor、ThreadPoolExecutor和FutureTask。

但他们没有提到,使用单独的线程作为服务也是一个不错的选择。这意味着可以在后台继续执行任务,无论用户做什么。(例如,如果他上传某个文件,你不想因为他离开了活动而停止这个过程)

这里有示例 - 找到RetainedFragment.java

这里有AsyncTask


这个问题已经过时得可怕了,我希望没有人认为它与现代事务相关。在这些年里,已经有许多库解决了这个问题。 - Jeremy Logan

-1
你可以在Android中使用Async Task。要进行http请求,你可以使用HttpURLConnection类。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接