最近在工作中遇到了一个Android的ANR问题,经过分析是WiFiStateMachine调用了系统函数readline(),然后出现了阻塞的现象,然后就深入了解了一下readline函数。网上搜了一下,发现关于readline()函数的解释大都是说readline()函数是阻塞函数,没有消息是并不会返回null,而是一直阻塞在那。至于阻塞的实质,都没有涉及,我经过仔细分析源码,得出结论如下:
我们先看readline函数源码,其中fill()函数才是真正读取数据的地方,只有读取完成之后,才会执行下面“/n”“/r”的判断,而读取数据的时候为什么阻塞,之前并没有人探究,那我们后面看看fill函数。
String readLine(boolean ignoreLF) throws IOException {
StringBuffer s = null;
int startChar;
synchronized (lock) {
ensureOpen();
boolean omitLF = ignoreLF || skipLF;
bufferLoop:
for (;;) {
if (nextChar >= nChars)
fill(); //真正读取数据的地方
if (nextChar >= nChars) { /* EOF */
if (s != null && s.length() > 0)
return s.toString();
else
return null;
}
...........其他
charLoop:
for (i = nextChar; i < nChars; i++) {
c = cb[i];
if ((c == '\n') || (c == '\r')) { //判断是否遇到\n或者\r,如果遇到就结束,作为一行数据输出。
eol = true;
break charLoop;
}
}
fill()函数中,可以看到读取数据是用了read(char[] cbuf, int off, int len) 来读取数据,当没有数据时,n值为0,while循环就不会结束,就会一直在此阻塞。
private void fill() throws IOException {
int dst;
if (markedChar <= UNMARKED) {
/* No mark */
dst = 0;
} else {
..............其他
int n;
do {
n = in.read(cb, dst, cb.length - dst);
} while (n == 0); //阻塞的关键
if (n > 0) {
nChars = dst + n;
nextChar = dst;
}
}
总结: 1、读入的数据要注意有/r或/n或/r/n 2、没有数据时会阻塞,在数据流异常或断开时才会返回null 3、readline()函数不会边读边输出,而是有一个缓冲区,读出的数据先放到缓冲区,遇到/r或/n或/r/n后再输出。
源码路径:xref: /libcore/ojluni/src/main/java/java/io/BufferedReader.java