2010-03-22 165 views
129

我正在向我的一个android应用程序的网站发出HTTP请求。Android从输入流有效读取

我正在使用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所以文件大小不大。有一个更好的方法吗?

感谢

安迪

回答

316

的在你的代码是,它创建了大量的重String对象,复制其内容并对其执行操作的问题。相反,您应该使用StringBuilder以避免在每个追加上创建新的String对象,并避免复制char数组。对于你的情况的实施将是这样的:

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

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

字符串结果=总的ToString();

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

  • a += b(或a = a + b),其中ab是字符串,拷贝ab内容到一个新的对象(请注意,您也正在复制a,其中包含累计String),并且您在每次迭代中都执行这些副本。
  • a.append(b),其中aStringBuilder,直接追加b内容a,这样你就不会在每次迭代累计字符串复制。
+0

正是!这也在Java有效项目51中进行了解释。我曾尝试使用分析并使用StringBuilder而不是String来节省大量处理时间。 – 2011-12-08 13:24:31

+20

对于奖励积分,提供初始容量以避免在StringBuilder填充时重新分配: 'StringBuilder total = new StringBuilder(inputStream.available());' – dokkaebi 2012-01-22 07:04:02

+10

这不会删除换行符吗? – schwiz 2012-05-10 04:35:50

1

如果该文件是漫长的,你可以通过附加到一个StringBuilder,而不是使用在各行中字符串连接优化代码。

+0

它不是很久,说实话任何类 - 它的网站WWW的网页源代码。 cokezone.co.uk - 真的没那么大。绝对小于100kb。 – RenegadeAndy 2010-03-22 12:57:37

+0

有没有人有任何其他的想法如何可以提高效率 - 或者如果这样做效率低下?如果后者是真的 - 为什么需要这么长时间?我不相信这个联系是怪罪。 – RenegadeAndy 2010-03-22 18:06:29

3

也许宁可读一遍'一行'并加入字符串,尝试'读取所有可用的',以避免扫描结束行,并避免字符串连接。

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

+0

嗯。所以它会是这样的: int offset = 5000; Byte [] bArr = new Byte [100]; Byte [] total = Byte [5000]; while(InputStream.available){offset} = InputStream.read(bArr,offset,100);对于(int i = 0; i RenegadeAndy 2010-03-24 12:13:50

+2

no no no no,我的意思是{byte total [] = new [instrm.available()]; instrm.read(总的,0,total.length); } 如果你需要它作为一个字符串,使用{String asString = String(total,0,total.length,“utf-8”); //假设为utf8 :-)} – SteelBytes 2010-03-25 00:44:14

5

这个怎么样。似乎会有更好的表现。

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和莫里斯·佩里的

+0

问题是 - 我不知道在开始之前我读的东西的大小 - 所以可能需要某种形式的数组增长。无论如何,你都可以通过http查询InputStream或URL来找出im检索的东西有多大,以优化字节数组的大小。我必须在移动设备上保持高效,这是主要问题! 但是,感谢那个想法 - 今晚会给它一个镜头,让你知道它如何处理性能增益! – RenegadeAndy 2010-03-24 17:40:05

+0

我不认为传入流的大小是那么重要。上面的代码一次读取1000个字节,但您可以增加/减少该大小。通过我的测试,并没有发现我使用了1000/10000字节的天气。尽管这只是一个简单的Java应用程序。在移动设备上可能更重要。 – Adrian 2010-03-24 18:18:01

+4

你可能会得到一个被切成两个后续读取的Unicode实体。更好地阅读,直到某种边界字符,如\ n,这正是BufferedReader所做的。 – 2011-10-16 19:41:18

32

您是否尝试过内置方法将流转换为字符串?它是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

阿帕奇下议院IO库可以从这里下载: http://commons.apache.org/io/download_io.cgi

+0

我意识到这是一个迟到的反应,但刚刚通过Google搜索偶然发现了这一点。 – Makotosan 2010-08-24 00:28:34

+54

Android API不包含IOUtils – 2010-11-10 12:09:12

+2

正确的,这就是为什么我提到外部库有它。我将库添加到了我的Android项目中,并且可以轻松地从流中读取数据。 – Makotosan 2010-11-15 18:49:50

2

一次读取一行文本,并将所述行分别附加到一个字符串中,这在提取每一行时都非常耗时,并且s o许多方法调用。

我能够通过分配一个相当大的字节数组来保存流数据,并且在需要时迭代地用一个更大的数组替换,并尝试读取与数组一样多的数据,从而获得更好的性能。

由于某些原因,当代码使用由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(); 
    } 
} 

编辑:事实证明,如果你不需要有内容重新编码(即你想要的内容AS IS),你应该不使用任何读者的子类。只需使用适当的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]; 
+0

这比上述和接受的答案快得多。你如何使用Android上的“Reader”和“Stream”? – SteveGSD 2014-05-22 00:30:27

5

我相信这是足够的效率......为了得到一个InputStream一个String,我会打电话给下面的方法:

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设置为参数。

4

可能比海梅·索里亚诺的回答稍快,没有阿德里安的答案的多字节编码的问题,我建议:

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(); 
} 
+0

你能解释为什么它会更快吗? – 2016-02-02 05:32:23

+0

它不会扫描输入的换行符,而只是读取1024个字节的数据块。我不认为这会造成任何实际的差异。 – heiner 2016-02-02 10:41:26

+0

对@Ronald的任何评论回答?他正在做同样的事情,但是对于一个大于inputStream大小的块。如果我扫描字符数组而不是字符数组作为Nikola的答案,它又有多不同? 其实我只是想知道哪种方法在哪种情况下最好? readLine删除\ n和\ r,但我甚至看到他们正在使用的google io应用程序代码readline – 2016-02-02 12:54:10

13

另一种可能性与番石榴:

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

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

String total = new String(ByteStreams.toByteArray(inputStream)); 
-1

我用它来读取完整数据:

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

https://developer.android.com/reference/java/io/InputStream.html#available() – AlexeyVMP 2017-01-20 09:52:37

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); 
1

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

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); 

完整