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.
Note that
use
will have closed theAutoCloseable
by the time the function returns.Also, when you open a
JsonReader
you are using theZipInputStream
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 theZipInputStream
object for that matter. The object holding the "actual" resources that must be released in a timely manner is the (I assume)InputStream
returned bymanager.open(...)
. You wrap that stream in aBufferedInputStream
which you close viause
, releasing any resources held by the stream. This means you should be able to remove the use ofuse
with theJsonReader
objects and your code will work (without having to worry about a resource leak).