当我使用BuffferedReader时为什么关闭InputStream?

时间:2015-07-20 15:47:41

标签: java android inputstream bufferedreader

当我读取InputStream并尝试再次使用它时,我收到此错误。

<pack name="Pack" id="pack" required="yes">
  <description>Pack</description>
  <executable targetfile="${INSTALL_PATH}/bin/run.sh" os="unix" stage="never" failure="warn" keep="true" />
</pack>

我正在使用的代码是......

07-20 17:36:24.762  11253-11277/? W/System.err﹕ java.io.IOException
07-20 17:36:24.762  11253-11277/? W/System.err﹕ at java.io.InputStream.reset(InputStream.java:208)
07-20 17:36:24.762  11253-11277/? W/System.err﹕ at demo31.com.maps.GMapDirections$DownloadDocumentTask.doInBackground(GMapDirections.java:134)
07-20 17:36:24.762  11253-11277/? W/System.err﹕ at demo31.com.maps.GMapDirections$DownloadDocumentTask.doInBackground(GMapDirections.java:86)
07-20 17:36:24.762  11253-11277/? W/System.err﹕ at android.os.AsyncTask$2.call(AsyncTask.java:292)
07-20 17:36:24.762  11253-11277/? W/System.err﹕ at javautil.concurrent.FutureTask.run(FutureTask.java:237)
07-20 17:36:24.762  11253-11277/? W/System.err﹕ at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
07-20 17:36:24.762  11253-11277/? W/System.err﹕ at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
07-20 17:36:24.762  11253-11277/? W/System.err﹕ at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
07-20 17:36:24.762  11253-11277/? W/System.err﹕ at java.lang.Thread.run(Thread.java:818)
07-20 17:36:24.762  11253-11277/? D/GMapDirections﹕ Exception while downloading data: java.io.IOException

当我尝试解析iStream的内容时会启动异常,因为之前我已经使用BufferedReader读取了它。

@Override
protected Document doInBackground(ArrayList... latLngs) {
  Log.d(TAG, "DownloadDocumentTask(doInBackground): Estoy dentro del background!!!");
  try {
    HttpsURLConnection urlConnection;
    FileService file = new FileService();
    InputStream iStream;
    Document result;

    file.writeLog(TAG, GMapDirections.class.getName(), getUrlConnection());

    URL url = new URL(getUrlConnection());

    // Creating an http connection to communicate with url
    urlConnection = (HttpsURLConnection) url.openConnection();

    // Connecting to url
    urlConnection.setRequestMethod("POST");
    urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    urlConnection.setRequestProperty("charset", "utf-8");
    urlConnection.setRequestProperty("Accept", "application/xml");
    urlConnection.setDoOutput(true);
    urlConnection.setDoInput(true);
    urlConnection.setUseCaches(false);
    urlConnection.connect();

    //Return data
    iStream = urlConnection.getInputStream();

    //Display what returns POST request
    BufferedReader br = new BufferedReader(new InputStreamReader(iStream));
    StringBuilder sb = new StringBuilder();

    String line;
    while ((line = br.readLine()) != null) {
      sb.append(line + "\n");
    }
    br.close();
    file.writeLog(GMapDirections.TAG, "doInBackground", sb.toString());

    // Parse the data to a Document Object
    DocumentBuilder builder =  DocumentBuilderFactory.newInstance().newDocumentBuilder();
    result = builder.parse(iStream);
    urlConnection.disconnect();

    return result;
  } catch (Exception e) {
     e.printStackTrace();
     Log.d(TAG, "Exception while downloading data: " + e.toString());
  }
  return null;
}

那么,我如何使用DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); result = builder.parse(iStream); 阅读iStream,然后用它来解析内容而不会出错?

5 个答案:

答案 0 :(得分:2)

您的堆栈跟踪和代码似乎不匹配。从堆栈跟踪看起来,doInBackground递归调用自身,然后是InputStream.reset()。但我在你的代码中看不到任何电话。

关于重新阅读流的实际问题: 由于您显然已经尝试过(并且失败了)InputStream.reset(),因此该流中的数据量可能太大(或者您忘记调用InputStream.mark())。简单的方法是创建另一个连接+流并读取它。这确实意味着您将实际且低效地从URL传输两次数据。

更有效的方法:

  1. 将流的全部内容读入内存/或临时文件(取决于大小),然后将其用作数据源。

  2. 使用TeeInputStream(https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/input/TeeInputStream.html)随时复制流数据。

答案 1 :(得分:0)

如果您想再次阅读Stream,则必须通过调用iStream.reset()

重置它

来自InputStream文档:http://developer.android.com/reference/java/io/InputStream.html

  

public synchronized void reset()

     

在API级别1中添加

     

将此流停留在最后标记的位置。如果自设置标记以来读取的字节数大于为标记提供的限制,或者未设置标记,则抛出anIOException。

     

此实现始终抛出IOException,具体的子类应提供正确的实现。

     

<强>抛出

     

IOException如果此流已关闭或发生另一个IOException。

答案 2 :(得分:0)

这是因为你致电BufferedReader.close()。来自Javadocs,

  

关闭流并释放与其关联的所有系统资源。

我刚才看了一遍,因为我担心这样的序列:

new BufferedReader(new FileReader(...))

会泄漏FileReader的Reader句柄。事实证明,BufferedReader.close也关闭了基础读者。

答案 3 :(得分:0)

在您关闭所有流的位置添加finally部分。移动try块之外的流声明,即:

BufferedReader br;
InputStream iStream;
try {
    // your regular code, but remove this line as you'll close later
    // br.close();
} catch (Exception whatever) {
    //...
} finally {
    if (iStream != null) {
        iStream.close(); // TODO you need to catch exception here too
    }
}

答案 4 :(得分:0)

根据@emu的答案(见第1点)

  

更有效的方法:
  的 1。将流的全部内容读入内存/或临时文件(取决于大小),然后将其用作数据源。

最好的解决方案是......

        @Override
    protected Document doInBackground(Void... voids) {
        try {
            HttpsURLConnection urlConnection;
            FileService file = new FileService();
            InputStream iStream;
            Document result;

            file.writeLog(TAG, GMapDirections.class.getName(), getUrlConnection());


            URL url = new URL(getUrlConnection());

            // Creating an http connection to communicate with url
            urlConnection = (HttpsURLConnection) url.openConnection();

            // Connecting to url
            urlConnection.setRequestMethod("POST");
            urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            urlConnection.setRequestProperty("charset", "utf-8");
            urlConnection.setRequestProperty("Accept", "application/xml");
            urlConnection.setDoOutput(true);
            urlConnection.setDoInput(true);
            urlConnection.setUseCaches(false);
            urlConnection.connect();

            //Return data
            iStream = urlConnection.getInputStream();

            // Parse the data to a Document Object
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            result = builder.parse(iStream);
            urlConnection.disconnect();

            //Write data get it from POST request
            //Transform Document 2 InputStream
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            Source xmlSource = new DOMSource(result);
            Result outputTarget = new StreamResult(outputStream);
            TransformerFactory.newInstance().newTransformer().transform(xmlSource, outputTarget);
            iStream = new ByteArrayInputStream(outputStream.toByteArray());

            //Write document in a file
            BufferedReader br = new BufferedReader(new InputStreamReader(iStream));
            StringBuilder sb = new StringBuilder();

            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line + "\n");
            }
            br.close();
            file.writeLog(GMapDirections.TAG, "doInBackground", sb.toString());

            //Return Document with data
            return result;

        } catch (Exception e) {
            e.printStackTrace();
            Log.d(TAG, "Exception while downloading data: " + e.toString());
        }

        return null;
    }