How to properly iterate through all entries of a ZipInputStream?

1.2k views Asked by At

On Android, I'm attempting to read a JSON file that is contained in a zip file which lives in my assets folder. The issue I'm running into is that it will throw an exception of java.io.IOException: Stream closed when I attempt to grab the non-existant nextEntry of the zipInputStream. Here is the block of code in question:

val awesomeList: ArrayList<AwesomeItem> = arrayListOf()

BufferedInputStream(manager.open("appData.zip")).use { bufferedInputStream ->
    ZipInputStream(bufferedInputStream).use { zipInputStream ->
        var zipEntry = zipInputStream.nextEntry
        while (zipEntry != null) {
            if (zipEntry.name == "awesomeList.json") {
                JsonReader.of(Okio.buffer(Okio.source(zipInputStream))).use { reader ->
                    awesomeItems.addAll(readAwesomeArray(reader))
                }
            }
            zipEntry = zipInputStream.nextEntry
        }
    }
}

private fun readAwesomeArray(reader: JsonReader): List<AwesomeItems> {
    ...
}

After stepping through the code using the debugger, the error gets thrown on the line zipEntry = zipInputStream.nextEntry after it has iterated through each file in the zip file (there are 4). I'm using Moshi's JsonReader to read the json file, and I know that works by inspecting the awesomeItems object when debugging paused using a breakpoint.

I'm familiar enough with Kotlin's use function to know that it will automatically close a stream when it is finished, but I am not sure which stream is getting closed, when it's getting closed, and why.

I'm interested in knowing the correct way to read a zip file's entries (from Android's assets folder) using Kotlin without throwing any exceptions. Most if not all of the examples on the web are in Java.

1

There are 1 answers

1
Slaw On BEST ANSWER

Note that use will have closed the AutoCloseable by the time the function returns.

val input: InputStream = ...
input.use { ... }
// 'input' has been closed by this point

Also, when you open a JsonReader you are using the ZipInputStream as its source. That means when the JSON reader is closed so too is the ZIP stream. This leads to your error since you try to process more data from the ZIP stream after closing the JSON reader.

I can't guarantee this, but I believe in this case you don't have to worry about closing the JsonReader objects, or the ZipInputStream object for that matter. The object holding the "actual" resources that must be released in a timely manner is the (I assume) InputStream returned by manager.open(...). You wrap that stream in a BufferedInputStream which you close via use, releasing any resources held by the stream. This means you should be able to remove the use of use with the JsonReader objects and your code will work (without having to worry about a resource leak).