2

我想知道这两种从 http 调用获取响应的方式的一般区别在哪里。

以下是 Android 示例,但在 Java 中完全一样。

org.apache.http仅使用库的第一个示例:

String s = "";

try {

    HttpPost httppost = new HttpPost(url);
    HttpClient httpclient = new DefaultHttpClient();
    HttpResponse response = httpclient.execute(httppost);
    HttpEntity entity = response.getEntity();

    s = EntityUtils.toString(entity, HTTP.UTF_8);

    // do something with s      
} catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

使用 BufferedReader 获取响应的第二个示例:

HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(url);
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();

InputStream instream = entity.getContent();
BufferedReader bufferedreader = new BufferedReader(new InputStreamReader(
                instream));

try {
    String line;
    StringBuilder sb = new StringBuilder();
    while((line = bufferedreader.readLine()) != null)
            sb.append(line);

        // do something with the stringbuilder

} catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

在哪种情况下,我应该更喜欢使用 Stream 和 Reader 的第二个示例而不是第一个示例,反之亦然?为什么我问这个问题有一些主要的想法。如果没有包括重要的一点,请告诉我:

  1. Reader 方法需要更多的空间和时间。
  2. 响应数据的大小对第一个或第二个示例没有影响。
  3. 第二个示例中的代码更复杂(开销更大)。
  4. Reader 方法更加灵活,因为图书馆中有大量不同的 Reader。

这一点使我赞成第一个例子。

4

2 回答 2

3

两个版本的性能差异很可能对您的应用程序的整体性能影响不大。(如果响应体非常大,这可能很重要,但即便如此,性能也可能会受到网络延迟和带宽考虑的支配。)

假设性能差异不太可能很显着,另一件要考虑的事情是代码的可维护性;即阅读和修改有多容易。我的感觉是第一个版本更简单,因此更容易正确和易于维护。


1)阅读器方法需要更多的空间和时间。

不必要。差异(如果有的话)将在于在创建结果字符串之前累积数据的方式。如果 HTTP 响应包含 Content-length 标头,则第一个版本可能会预先分配byte[]正确大小的 a。相比之下,第二个版本在 a 中累积数据,StringBuilder这可能需要对构建器的支持数组进行多轮重新分配/复制。(额外的工作O(N)不会改变计算复杂性的整体度量。但它仍然会影响性能......和内存使用。)

2)响应数据的大小对第一个或第二个例子没有影响。

不正确。它对两种情况都有影响。(显然……长响应比短响应需要更长的时间……在这两种情况下!!)

3)第二个例子中的代码更复杂(开销更大)。

它更复杂,但这并不意味着有更多的开销。在第一种情况下,也有复杂性,但它不是必须编写的代码。但无论如何,简单并不一定意味着更高的效率/更低的开销。这不是性能的运作方式......

4) Reader 方法更加灵活,因为图书馆中有大量不同的读者。

无关紧要。在这种情况下,您对Reader类做出了很好的选择。灵活性在这里没有任何区别。


还有其他问题:

  • 两个版本都假定响应正文是文本。
  • 第一个版本假定响应正文以 UTF-8 编码。
  • 第二个版本假定响应正文使用(本地)平台的默认编码进行编码。
  • 第二个版本吞噬了换行符。那是一个错误。

错误说明:readLine() 方法读取一行并返回,不带行分隔符。然后,您的代码将其添加到StringBuilder... 而不添加行分隔符。最终结果是StringBuilder包含删除了所有行分隔符的数据。哎呀!

于 2013-09-26T12:10:59.000 回答
2

第一个示例使用 ApacheEntityUtils读取字符串中的流。从代码中,它的作用是使用 aReader以固定大小char[]从 中读取内容entity.getContent(),并将其附加到 Buffer 的形式中。

第二个示例使用BufferedReader.

  1. 本身可能不会占用更多空间,但是缓冲所需的BufferedReader空间量是未知的,因为它需要读取一行(即直到找到换行符),并且响应中可能没有任何换行符。整行被缓冲,然后读取到字符串,然后附加,使其在内存中出现 3 次。

  2. 同意

  3. 示例的代码BufferedReader显然更长。

  4. 任何不使用第三方方法的方法都更加灵活,因为您可以处理流的内容而无需等待阅读整个内容

  5. readLine方法还有另一个缺陷,即丢失换行符。在某些情况下,它们可能携带信息。

如果您想要一种高效而灵活的方法,请查看 apache EntityUtils的实际实现。它使用固定大小的 char[] 进行缓冲,确保您知道内存开销的数量,并且它读取所有字符,包括换行符。

readLine仅当您需要阅读一行时才真正有用。(!)

(实现的相关部分:)

    Reader reader = new InputStreamReader(instream, charset);
    CharArrayBuffer buffer = new CharArrayBuffer(i);
    try {
        char[] tmp = new char[1024];
        int l;
        while((l = reader.read(tmp)) != -1) {
            buffer.append(tmp, 0, l);
        }
    } finally {
        reader.close();
    }
于 2013-09-26T12:00:39.633 回答