Android 高效地从输入流中读取数据

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

谢谢

安迪

274235 次浏览

如果文件很长,可以通过附加到 StringBuilder 而不是对每行使用 String 串联来优化代码。

也许应该读取“每次一行”并加入字符串,尝试“读取所有可用的内容”,以避免扫描行尾,也避免字符串加入。

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

这个怎么样,看起来性能更好。

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

编辑: 实际上,这种类型包括两个钢字节和莫里斯佩里的

代码中的问题在于,它创建了大量沉重的 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,只需添加:

字符串 result = total. toString () ;

我会试着解释得更清楚。

  • a += b(或 a = a + b) ,其中 ab是字符串,将 都有 a 还有 b的内容复制到一个新的对象(注意,您也在复制包含 a = a + b0 Stringa) ,并且在每次迭代中执行这些复制。
  • a.append(b),其中 aStringBuilder,直接将 b内容附加到 a,因此不需要在每次迭代时复制累积的字符串。

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

那么你的代码就是这一行:

String total = IOUtils.toString(inputStream);

相关文档可以在这里找到: Http://commons.apache.org/io/api-1.4/org/apache/commons/io/ioutils.html#tostring%28java.io 输入流程% 29

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

一次读取一行文本,然后将该行逐行附加到一个字符串中,这不仅耗费提取每一行文本的时间,而且耗费大量方法调用的开销。

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

出于某种原因,当代码使用 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];

我相信这已经足够有效了... ... 要从 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 的答案中的多字节编码问题,我建议:

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

番石榴的另一种可能性是:

从属关系: compile 'com.google.guava:guava:11.0.2'

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


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

我习惯于阅读完整的数据:

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

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

    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,我们使用 ReadLine () 方法。我们迭代直到 < em > 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);

完整