Java Android 从输入流中高效读取
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2492076/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android Reading from an Input stream efficiently
提问by RenegadeAndy
I am making an HTTP get request to a website for an android application I am making.
我正在向网站发出 HTTP get 请求以获取我正在制作的 android 应用程序。
I am using a DefaultHttpClient and using HttpGet to issue the request. I get the entity response and from this obtain an InputStream object for getting the html of the page.
我正在使用 DefaultHttpClient 并使用 HttpGet 发出请求。我得到实体响应,并从中获取一个 InputStream 对象来获取页面的 html。
I then cycle through the reply doing as follows:
然后我循环查看回复,如下所示:
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
String x = "";
x = r.readLine();
String total = "";
while(x!= null){
total += x;
x = r.readLine();
}
However this is horrendously slow.
然而,这是非常缓慢的。
Is this inefficient? I'm not loading a big web page - www.cokezone.co.ukso the file size is not big. Is there a better way to do this?
这是低效的吗?我没有加载一个大网页 - www.cokezone.co.uk所以文件大小不大。有一个更好的方法吗?
Thanks
谢谢
Andy
安迪
采纳答案by Jaime Soriano
The problem in your code is that it's creating lots of heavy String
objects, copying their contents and performing operations on them. Instead, you should use StringBuilder
to avoid creating new String
objects on each append and to avoid copying the char arrays. The implementation for your case would be something like this:
您的代码中的问题在于它创建了大量重String
对象,复制它们的内容并对它们执行操作。相反,您应该StringBuilder
避免String
在每次追加时创建新对象并避免复制字符数组。您的案例的实现将是这样的:
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder total = new StringBuilder();
for (String line; (line = r.readLine()) != null; ) {
total.append(line).append('\n');
}
You can now use total
without converting it to String
, but if you need the result as a String
, simply add:
您现在可以使用total
而无需将其转换为String
,但如果您需要将结果作为String
,只需添加:
String result = total.toString();
字符串结果 = total.toString();
I'll try to explain it better...
我会试着更好地解释它......
a += b
(ora = a + b
), wherea
andb
are Strings, copies the contents of botha
andb
to a new object (note that you are also copyinga
, which contains the accumulatedString
), and you are doing those copies on each iteration.a.append(b)
, wherea
is aStringBuilder
, directly appendsb
contents toa
, so you don't copy the accumulated string at each iteration.
a += b
(或a = a + b
),其中a
和b
都是字符串,副本的内容都a
和b
一个新的对象(注意,你还复制a
,它包含累积String
),和你正在做的每一次迭代的拷贝。a.append(b)
,其中a
是 aStringBuilder
,直接将b
内容附加到a
,因此您不会在每次迭代时复制累积的字符串。
回答by Maurice Perry
If the file is long, you can optimize your code by appending to a StringBuilder instead of using a String concatenation for each line.
如果文件很长,您可以通过附加到 StringBuilder 而不是对每一行使用字符串连接来优化代码。
回答by SteelBytes
Maybe rather then read 'one line at a time' and join the strings, try 'read all available' so as to avoid the scanning for end of line, and to also avoid string joins.
也许不如读取“一次一行”并连接字符串,尝试“读取所有可用的”以避免扫描行尾,也避免字符串连接。
ie, InputStream.available()
and InputStream.read(byte[] b), int offset, int length)
即,InputStream.available()
和InputStream.read(byte[] b), int offset, int length)
回答by Adrian
What about this. Seems to give better 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));
}
Edit: Actually this sort of encompasses both steelbytes and Maurice Perry's
编辑:实际上这种类型包括 steelbytes 和 Maurice Perry's
回答by Makotosan
Have you tried the built in method to convert a stream to a string? It's part of the Apache Commons library (org.apache.commons.io.IOUtils).
您是否尝试过将流转换为字符串的内置方法?它是 Apache Commons 库 (org.apache.commons.io.IOUtils) 的一部分。
Then your code would be this one line:
那么你的代码就是这一行:
String total = IOUtils.toString(inputStream);
The documentation for it can be found here: http://commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html#toString%28java.io.InputStream%29
它的文档可以在这里找到:http: //commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html#toString%28java.io.InputStream%29
The Apache Commons IO library can be downloaded from here: http://commons.apache.org/io/download_io.cgi
Apache Commons IO 库可以从这里下载:http: //commons.apache.org/io/download_io.cgi
回答by Huperniketes
Reading one line of text at a time, and appending said line to a string individually is time-consuming both in extracting each line and the overhead of so many method invocations.
一次读取一行文本,并将该行单独附加到一个字符串中,在提取每一行和如此多的方法调用的开销方面都很耗时。
I was able to get better performance by allocating a decent-sized byte array to hold the stream data, and which is iteratively replaced with a larger array when needed, and trying to read as much as the array could hold.
通过分配一个合适大小的字节数组来保存流数据,我能够获得更好的性能,并且在需要时用更大的数组迭代替换,并尝试读取数组可以容纳的尽可能多的内容。
For some reason, Android repeatedly failed to download the entire file when the code used the InputStream returned by HTTPUrlConnection, so I had to resort to using both a BufferedReader and a hand-rolled timeout mechanism to ensure I would either get the whole file or cancel the transfer.
出于某种原因,当代码使用 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();
}
}
EDIT:It turns out that if you don't need to have the content re-encoded (ie, you want the content AS IS) you shouldn't use any of the Reader subclasses. Just use the appropriate Stream subclass.
编辑:事实证明,如果您不需要重新编码内容(即,您想要内容原样),则不应使用任何 Reader 子类。只需使用适当的 Stream 子类。
Replace the beginning of the preceding method with the corresponding lines of the following to speed it up an extra 2 to 3 times.
将前面方法的开头替换为下面的相应行,使其速度额外提高 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];
回答by Budimir Grom
I believe this is efficient enough... To get a String from an InputStream, I'd call the following method:
我相信这足够有效......要从 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();
}
I always use UTF-8. You could, of course, set charset as an argument, besides InputStream.
我总是使用 UTF-8。当然,除了 InputStream 之外,您还可以将 charset 设置为参数。
回答by heiner
Possibly somewhat faster than Jaime Soriano's answer, and without the multi-byte encoding problems of Adrian's answer, I suggest:
可能比 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();
}
回答by Andrey
Another possibility with Guava:
番石榴的另一种可能性:
dependency: compile 'com.google.guava:guava:11.0.2'
依赖: compile 'com.google.guava:guava:11.0.2'
import com.google.common.io.ByteStreams;
...
String total = new String(ByteStreams.toByteArray(inputStream ));
回答by Ronald
I am use to read full data:
我用来读取完整数据:
// inputStream is one instance InputStream
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
String dataString = new String(data);