【问题标题】:Android Reading from an Input stream efficientlyAndroid有效地从输入流中读取
【发布时间】:2011-01-30 07:53:15
【问题描述】:

我正在为我正在制作的 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 所以文件大小不大。有没有更好的方法来做到这一点?

谢谢

安迪

【问题讨论】:

标签: java android input stream bufferedreader


【解决方案1】:

您的代码中的问题是,它创建了大量的String 对象,复制它们的内容并对它们执行操作。相反,您应该使用 StringBuilder 来避免在每次追加时创建新的 String 对象并避免复制 char 数组。您的案例的实现将是这样的:

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),其中ab 是字符串,将both a b 的内容复制到新的对象(请注意,您还复制了a,其中包含累积 String),并且您在每次迭代中都进行这些复制。
  • a.append(b),其中aStringBuilder,直接将b 的内容附加到a,这样就不会在每次迭代时复制累积的字符串。

【讨论】:

  • 对于奖励积分,提供初始容量以避免在 StringBuilder 填满时重新分配:StringBuilder total = new StringBuilder(inputStream.available());
  • 这不是删掉换行符吗?
  • 不要忘记将 while 包裹在 try / catch 中,如下所示:try { while ((line = r.readLine()) != null) { total.append(line); } } catch (IOException e) { Log.i(tag, "problem with readline in inputStreamToString function"); }
  • @botbot:记录和忽略异常并不比仅仅忽略异常好多少......
  • 令人惊奇的是,Android 没有内置的流到字符串的转换。让网络上的每个代码 sn-p 和地球上的应用程序重新实现 readline 循环是荒谬的。这种模式应该在 70 年代随着豌豆绿消失。
【解决方案2】:

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

【讨论】:

  • 我意识到这是一个迟到的回应,但刚刚通过谷歌搜索偶然发现了这个。
  • android API 不包含 IOUtils
  • 对,这就是我提到拥有它的外部库的原因。我将该库添加到我的 Android 项目中,它可以轻松地从流中读取。
  • 我在哪里可以下载这个,你是如何将它导入到你的android项目中的?
  • 如果你必须下载它,我不会称它为“内置”;不过,我只是下载了它,并会试一试。
【解决方案3】:

番石榴的另一种可能性:

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

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

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

【讨论】:

    【解决方案4】:

    我相信这已经足够高效了...要从 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 设置为参数。

    【讨论】:

      【解决方案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 和 Maurice Perry 的

      【讨论】:

      • 问题是 - 在我开始之前我不知道我正在阅读的内容的大小 - 所以可能还需要某种形式的数组增长。除非您可以通过 http 查询 InputStream 或 URL,以了解我检索的内容有多大,以优化字节数组的大小。我必须在移动设备上保持高效,这是主要问题!不过感谢您的想法 - 今晚会试一试,让您知道它在性能提升方面的处理方式!
      • 我认为传入流的大小并不那么重要。上面的代码一次读取 1000 个字节,但您可以增加/减少该大小。通过我的测试,我使用 1000/10000 字节的天气并没有太大的不同。不过,那只是一个简单的 Java 应用程序。它在移动设备上可能更重要。
      • 您最终可能会得到一个 Unicode 实体,该实体被分成两个后续读取。更好地阅读直到某种边界字符,例如 \n,这正是 BufferedReader 所做的。
      【解决方案6】:

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

      【讨论】:

      • 你能解释一下为什么它会更快吗?
      • 它不会扫描输入中的换行符,而只是读取 1024 字节的块。我并不是说这会产生任何实际影响。
      • @Ronald 答案的任何 cmets 吗?他也在做同样的事情,但更大的块等于 inputStream 大小。另外,如果我扫描 char 数组而不是 byte 数组作为 Nikola 的回答,这又有什么不同?其实我只是想知道在哪种情况下哪种方法最好? readLine 也删除了 \n 和 \r 但我什至看到了他们正在使用 readline 的 google io 应用程序代码
      【解决方案7】:

      也许与其读取“一次一行”并加入字符串,不如尝试“读取所有可用”以避免扫描行尾,并避免字符串连接。

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

      【讨论】:

      • 嗯。所以它会是这样的: int offset = 5000;字节[] bArr = 新字节[100];字节[]总=字节[5000]; while(InputStream.available){ offset = InputStream.read(bArr,offset,100); for(int i=0;i
      • 不不不不,我的意思是简单的 { byte total[] = new [instrm.available()]; instrm.read(total,0,total.length); } 如果您随后需要它作为字符串,请使用 { String asString = String(total,0,total.length,"utf-8"); // 假设 utf8 :-) }
      【解决方案8】:

      一次读取一行文本,并将所述行单独附加到一个字符串中,在提取每一行和如此多的方法调用的开销方面都非常耗时。

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

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

      【讨论】:

      • 这比上述和接受的答案要快得多。你如何在android上使用“Reader”和“Stream”?
      【解决方案9】:

      如果文件很长,您可以通过附加到 StringBuilder 来优化您的代码,而不是对每一行使用字符串连接。

      【讨论】:

      • 说实话没那么久——它是网站 www.cokezone.co.uk 的页面来源——所以真的没那么大。绝对小于 100kb。
      • 是否有人对如何提高效率有任何其他想法 - 或者如果这甚至是低效的!?如果后者是真的 - 为什么需要这么长时间?我不认为这种联系是罪魁祸首。
      【解决方案10】:
          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);
      

      【讨论】:

        【解决方案11】:

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

        完成

        【讨论】:

          【解决方案12】:

          我是用来读取完整数据的:

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

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

          【讨论】:

            猜你喜欢
            • 2016-09-02
            • 2013-06-03
            • 1970-01-01
            • 2011-02-12
            相关资源
            最近更新 更多