160

我正在为我正在制作的 android 应用程序向网站发出 HTTP 获取请求。

我正在使用 DefaultHttpClient 并使用 HttpGet 发出请求。我得到实体响应,并从中获得一个 InputStream 对象以获取页面的 html。

然后,我按如下方式循环浏览回复:

BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
String x = "";
x = r.readLine();
String total = "";

while(x!= null){
total += x;
x = r.readLine();
}

然而,这是非常缓慢的。

这是低效的吗?我没有加载一个大网页 - www.cokezone.co.uk所以文件大小不大。有一个更好的方法吗?

谢谢

安迪

4

12 回答 12

362

您的代码中的问题是它创建了许多重String对象,复制它们的内容并对它们执行操作。相反,您应该StringBuilder避免String在每次追加时创建新对象并避免复制 char 数组。您的案例的实现将是这样的:

BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder total = new StringBuilder();
for (String line; (line = r.readLine()) != null; ) {
    total.append(line).append('\n');
}

您现在可以total在不将其转换为 的情况下使用String,但如果您需要将结果作为String,只需添加:

字符串结果 = total.toString();

我会尝试更好地解释它...

  • a += b(或a = a + b),其中ab是字符串,将 和 的内容复制 a 一个 b新对象(请注意,您也在复制a,其中包含累积 String的),并且您在每次迭代中都进行这些复制。
  • a.append(b),其中a是 a ,直接将内容StringBuilder附加到,因此您不会在每次迭代时复制累积的字符串。ba
于 2010-03-30T22:42:01.597 回答
35

您是否尝试过将流转换为字符串的内置方法?它是 Apache Commons 库 (org.apache.commons.io.IOUtils) 的一部分。

那么您的代码将是这一行:

String total = IOUtils.toString(inputStream);

它的文档可以在这里找到: http ://commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html#toString%28java.io.InputStream%29

Apache Commons IO 库可以从这里下载: http ://commons.apache.org/io/download_io.cgi

于 2010-08-24T00:27:13.893 回答
16

番石榴的另一种可能性:

依赖:compile 'com.google.guava:guava:11.0.2'

import com.google.common.io.ByteStreams;
...

String total = new String(ByteStreams.toByteArray(inputStream ));
于 2015-04-20T20:48:32.343 回答
11

我相信这足够有效......要从 InputStream 中获取字符串,我将调用以下方法:

public static String getStringFromInputStream(InputStream stream) throws IOException
{
    int n = 0;
    char[] buffer = new char[1024 * 4];
    InputStreamReader reader = new InputStreamReader(stream, "UTF8");
    StringWriter writer = new StringWriter();
    while (-1 != (n = reader.read(buffer))) writer.write(buffer, 0, n);
    return writer.toString();
}

我总是使用 UTF-8。当然,除了 InputStream 之外,您还可以将 charset 设置为参数。

于 2014-08-16T20:21:09.937 回答
7

那这个呢。似乎可以提供更好的性能。

byte[] bytes = new byte[1000];

StringBuilder x = new StringBuilder();

int numRead = 0;
while ((numRead = is.read(bytes)) >= 0) {
    x.append(new String(bytes, 0, numRead));
}

编辑:实际上这种包括 Steelbytes 和 Maurice Perry 的

于 2010-03-24T16:04:31.727 回答
4

可能比 Jaime Soriano 的回答要快一些,并且没有 Adrian 回答的多字节编码问题,我建议:

File file = new File("/tmp/myfile");
try {
    FileInputStream stream = new FileInputStream(file);

    int count;
    byte[] buffer = new byte[1024];
    ByteArrayOutputStream byteStream =
        new ByteArrayOutputStream(stream.available());

    while (true) {
        count = stream.read(buffer);
        if (count <= 0)
            break;
        byteStream.write(buffer, 0, count);
    }

    String string = byteStream.toString();
    System.out.format("%d bytes: \"%s\"%n", string.length(), string);
} catch (IOException e) {
    e.printStackTrace();
}
于 2015-03-21T22:47:28.520 回答
3

也许不如读取“一次一行”并加入字符串,尝试“读取所有可用”以避免扫描行尾,并避免字符串连接。

即,InputStream.available()InputStream.read(byte[] b), int offset, int length)

于 2010-03-24T00:46:43.453 回答
2

一次读取一行文本,并将所述行单独附加到字符串中,无论是提取每一行还是大量方法调用的开销,都非常耗时。

通过分配一个大小合适的字节数组来保存流数据,我能够获得更好的性能,并在需要时用更大的数组迭代替换,并尝试读取数组可以容纳的尽可能多的内容。

由于某种原因,当代码使用 HTTPUrlConnection 返回的 InputStream 时,Android 反复无法下载整个文件,所以我不得不求助于使用 BufferedReader 和手动超时机制来确保我要么获取整个文件,要么取消转移。

private static  final   int         kBufferExpansionSize        = 32 * 1024;
private static  final   int         kBufferInitialSize          = kBufferExpansionSize;
private static  final   int         kMillisecondsFactor         = 1000;
private static  final   int         kNetworkActionPeriod        = 12 * kMillisecondsFactor;

private String loadContentsOfReader(Reader aReader)
{
    BufferedReader  br = null;
    char[]          array = new char[kBufferInitialSize];
    int             bytesRead;
    int             totalLength = 0;
    String          resourceContent = "";
    long            stopTime;
    long            nowTime;

    try
    {
        br = new BufferedReader(aReader);

        nowTime = System.nanoTime();
        stopTime = nowTime + ((long)kNetworkActionPeriod * kMillisecondsFactor * kMillisecondsFactor);
        while(((bytesRead = br.read(array, totalLength, array.length - totalLength)) != -1)
        && (nowTime < stopTime))
        {
            totalLength += bytesRead;
            if(totalLength == array.length)
                array = Arrays.copyOf(array, array.length + kBufferExpansionSize);
            nowTime = System.nanoTime();
        }

        if(bytesRead == -1)
            resourceContent = new String(array, 0, totalLength);
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }

    try
    {
        if(br != null)
            br.close();
    }
    catch(IOException e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

编辑:事实证明,如果您不需要重新编码内容(即,您希望内容原样),则不应使用任何 Reader 子类。只需使用适当的 Stream 子类。

将前面方法的开头替换为下面的相应行,可以将其速度提高2 到 3 倍

String  loadContentsFromStream(Stream aStream)
{
    BufferedInputStream br = null;
    byte[]              array;
    int                 bytesRead;
    int                 totalLength = 0;
    String              resourceContent;
    long                stopTime;
    long                nowTime;

    resourceContent = "";
    try
    {
        br = new BufferedInputStream(aStream);
        array = new byte[kBufferInitialSize];
于 2014-01-13T07:10:13.063 回答
1

如果文件很长,您可以通过附加到 StringBuilder 而不是对每一行使用字符串连接来优化代码。

于 2010-03-22T12:54:37.110 回答
1
    byte[] buffer = new byte[1024];  // buffer store for the stream
    int bytes; // bytes returned from read()

    // Keep listening to the InputStream until an exception occurs
    while (true) {
        try {
            // Read from the InputStream
            bytes = mmInStream.read(buffer);

            String TOKEN_ = new String(buffer, "UTF-8");

            String xx = TOKEN_.substring(0, bytes);
于 2016-06-29T21:47:32.640 回答
1

要将 InputStream 转换为 String,我们使用 BufferedReader.readLine()方法。我们迭代直到BufferedReader返回 null,这意味着没有更多数据要读取。每行都将附加到StringBuilder并作为 String 返回。

 public static String convertStreamToString(InputStream is) {

        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();

        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sb.toString();
    }
}`

最后从您要转换的任何类调用该函数

String dataString = Utils.convertStreamToString(in);

完全的

于 2016-08-30T16:32:13.207 回答
-2

我习惯于读取完整数据:

// inputStream is one instance InputStream
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
String dataString = new String(data);

请注意,这适用于存储在磁盘上的文件,而不适用于没有默认大小的流。

于 2015-12-31T15:30:22.460 回答