Skip to content

Commit c48b0b9

Browse files
author
Xavier Ducrohet
committed
Layoutlib: Read and close XML files as soon as possible.
Because passing an InputStream to KXML does not close the stream after the file has been parsed, the files are staying locked on windows until the gc and finalizers are run. This change preload the XML files and close their stream, and then pass the content in a stream to the parser. Change-Id: Iabe27989dc616ec9e7de88e52b1ec3af9f007f7c
1 parent 891b703 commit c48b0b9

File tree

1 file changed

+68
-3
lines changed

1 file changed

+68
-3
lines changed

tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@
2121
import org.xmlpull.v1.XmlPullParser;
2222
import org.xmlpull.v1.XmlPullParserException;
2323

24+
import java.io.BufferedInputStream;
25+
import java.io.ByteArrayInputStream;
2426
import java.io.File;
2527
import java.io.FileInputStream;
2628
import java.io.FileNotFoundException;
29+
import java.io.IOException;
2730
import java.io.InputStream;
2831

2932
/**
@@ -38,14 +41,21 @@ public class ParserFactory {
3841

3942
public static XmlPullParser create(File f)
4043
throws XmlPullParserException, FileNotFoundException {
41-
KXmlParser parser = instantiateParser(f.getName());
42-
parser.setInput(new FileInputStream(f), ENCODING);
43-
return parser;
44+
InputStream stream = new FileInputStream(f);
45+
return create(stream, f.getName(), f.length());
4446
}
4547

4648
public static XmlPullParser create(InputStream stream, String name)
49+
throws XmlPullParserException {
50+
return create(stream, name, -1);
51+
}
52+
53+
private static XmlPullParser create(InputStream stream, String name, long size)
4754
throws XmlPullParserException {
4855
KXmlParser parser = instantiateParser(name);
56+
57+
stream = readAndClose(stream, name, size);
58+
4959
parser.setInput(stream, ENCODING);
5060
return parser;
5161
}
@@ -61,6 +71,61 @@ private static KXmlParser instantiateParser(String name) throws XmlPullParserExc
6171
return parser;
6272
}
6373

74+
private static InputStream readAndClose(InputStream stream, String name, long size)
75+
throws XmlPullParserException {
76+
// just a sanity check. It's doubtful we'll have such big files!
77+
if (size > Integer.MAX_VALUE) {
78+
throw new XmlPullParserException("File " + name + " is too big to be parsed");
79+
}
80+
int intSize = (int) size;
81+
82+
// create a buffered reader to facilitate reading.
83+
BufferedInputStream bufferedStream = new BufferedInputStream(stream);
84+
try {
85+
int avail;
86+
if (intSize != -1) {
87+
avail = intSize;
88+
} else {
89+
// get the size to read.
90+
avail = bufferedStream.available();
91+
}
92+
93+
// create the initial buffer and read it.
94+
byte[] buffer = new byte[avail];
95+
int read = stream.read(buffer);
96+
97+
// this is the easy case.
98+
if (read == intSize) {
99+
return new ByteArrayInputStream(buffer);
100+
}
101+
102+
// check if there is more to read (read() does not necessarily read all that
103+
// available() returned!)
104+
while ((avail = bufferedStream.available()) > 0) {
105+
if (read + avail > buffer.length) {
106+
// just allocate what is needed. We're mostly reading small files
107+
// so it shouldn't be too problematic.
108+
byte[] moreBuffer = new byte[read + avail];
109+
System.arraycopy(buffer, 0, moreBuffer, 0, read);
110+
buffer = moreBuffer;
111+
}
112+
113+
read += stream.read(buffer, read, avail);
114+
}
115+
116+
// return a new stream encapsulating this buffer.
117+
return new ByteArrayInputStream(buffer);
118+
119+
} catch (IOException e) {
120+
throw new XmlPullParserException("Failed to read " + name, null, e);
121+
} finally {
122+
try {
123+
bufferedStream.close();
124+
} catch (IOException e) {
125+
}
126+
}
127+
}
128+
64129
private static class CustomParser extends KXmlParser {
65130
private final String mName;
66131

0 commit comments

Comments
 (0)