[英]Android Reading from an Input stream efficiently
我正在向网站发出 HTTP get 请求以获取我正在制作的 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所以文件大小不大。 有一个更好的方法吗?
谢谢
安迪
您的代码中的问题在于它创建了大量重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');
}
您现在可以使用total
而不将其转换为String
,但如果您需要将结果作为String
,只需添加:
字符串结果 = total.toString();
我会试着更好地解释它......
a += b
(或a = a + b
),其中a
和b
是字符串,副本两者的内容a
和b
到一个新的对象(需要注意的是也复制a
,其中包含累计String
),并且每次迭代都在做这些副本。a.append(b)
,其中a
是StringBuilder
,直接将b
内容附加到a
,因此您不会在每次迭代时复制累积的字符串。您是否尝试过将流转换为字符串的内置方法? 它是 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
番石榴的另一种可能性:
依赖: compile 'com.google.guava:guava:11.0.2'
import com.google.common.io.ByteStreams;
...
String total = new String(ByteStreams.toByteArray(inputStream ));
我相信这足够有效......要从 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 设置为参数。
那这个呢。 似乎提供了更好的性能。
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's
可能比 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();
}
也许而不是读取“一次一行”并连接字符串,尝试“读取所有可用的”以避免扫描行尾,也避免字符串连接。
即InputStream.available()
和InputStream.read(byte[] b), int offset, int length)
一次读取一行文本,并将该行单独附加到一个字符串中,在提取每一行和如此多的方法调用的开销方面都很耗时。
通过分配一个合适大小的字节数组来保存流数据,我能够获得更好的性能,并且在需要时迭代地替换为更大的数组,并尝试读取尽可能多的数组。
出于某种原因,当代码使用 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];
如果文件很长,您可以通过附加到 StringBuilder 而不是对每一行使用字符串连接来优化代码。
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并作为字符串返回。
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);
请注意,这适用于存储在磁盘上的文件,而不适用于没有默认大小的流。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.