Android从inputstream高效地读取
我正在向我的一个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所以文件大小不大。 有没有更好的方法来做到这一点?
谢谢
安迪
代码中的问题在于,它会创build大量繁重的String
对象,复制它们的内容并对它们执行操作。 相反,你应该使用StringBuilder
来避免在每个append上创build新的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
,只需添加:
String result = total.toString();
我会尽力解释它…
-
a += b
(或a = a + b
),其中a
和b
是string,将a
和b
的内容复制到一个新的对象中(注意你也复制a
包含累积String
),而你在每次迭代中都要做这些副本。 -
a.append(b)
,其中a
是一个StringBuilder
,将b
内容直接附加到a
,所以在每次迭代时不要复制累加的string。
你有没有尝试内置的方法来将一个stream转换为string? 它是Apache Commons库(org.apache.commons.io.IOUtils)的一部分。
那么你的代码将是这一行:
String total = IOUtils.toString(inputStream);
它的文档可以在这里find: 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
番石榴的另一种可能性:
依赖关系: compile 'com.google.guava:guava:11.0.2'
import com.google.common.io.ByteStreams; ... String total = new String(ByteStreams.toByteArray(inputStream ));
那这个呢。 似乎给予更好的performance。
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)); }
编辑:其实这种包括钢铁和莫里斯·佩里的
我相信这是足够高效的…要从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设置为参数。
可能比Jaime Soriano的回答稍微快一点,也没有Adrian的答案的多字节编码问题,我build议:
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(); }
也许宁可读“一次一行”并joinstring,尝试“读取所有可用的”,以避免扫描结束行,并避免string连接。
即InputStream.available()和InputStream.read(byte [] b,int offset,int length)
一次读取一行文本,并将所述行分别附加到一个string中,这对于提取每一行以及这么多方法调用的开销都是非常耗时的。
我能够通过分配一个相当大的字节数组来保存stream数据,并在需要时迭代地用一个更大的数组replace,并尝试读取与数组一样多的数据。
出于某种原因,当代码使用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(); } }
编辑:事实certificate,如果你不需要有内容重新编码(即,你想要的内容是原样 ),你不应该使用任何的读者子类。 只要使用适当的Stream子类即可。
将上述方法的开始replace为以下方法的相应行,以加快其速度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];
如果文件很长,可以通过附加到StringBuilder来优化代码,而不是使用每行的string连接。
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);
为了将InputStream转换为String,我们使用BufferedReader.readLine()方法。 我们迭代,直到BufferedReader返回null,这意味着没有更多的数据要读取。 每一行都会附加到一个StringBuilder中,并以String的forms返回。
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);
完成
我用来阅读完整的数据:
// inputStream is one instance InputStream byte[] data = new byte[inputStream.available()]; inputStream.read(data); String dataString = new String(data);