必须检查inputstream每次read()返回值,网络流不可裸用;文本用inputstreamreader+bufferedreader配指定编码,二进制用bytearrayoutputstream累积字节;android需额外关闭连接防泄漏。

直接用 InputStream 读网络数据会丢字节,别裸用
网络流(比如 URLConnection.getInputStream() 或 HttpURLConnection.getInputStream() 返回的)不是文件流,read() 调用可能只返回部分数据,甚至一次只返回几个字节。裸调 read(byte[]) 却不检查返回值,极易截断内容。
常见错误现象:new String(buffer) 得到乱码或内容被砍掉前半段;readLine() 在 UTF-8 中遇到换行符位置不对直接卡死;available() 返回 0 却误以为流空了。
- 永远不要依赖
available()判断是否读完 —— 它只是“当前可非阻塞读取的字节数”估计值,对网络流基本不准 - 不要用
read()单字节循环,性能差且易出错 - 必须检查每次
read(byte[], int, int)的返回值:len,它才是真实读到的字节数
InputStreamReader + BufferedReader 适合文本,但得设对编码
如果你确定远程资源是纯文本(如 JSON、XML、HTML),用字符流更安全。关键点不在“能不能用”,而在“怎么设编码才不崩”。
典型场景:读取 HTTP 接口返回的 UTF-8 编码 JSON 字符串。
- 必须显式传入
Charset.forName("UTF-8")或"UTF-8"字符串,不能依赖平台默认编码(Windows 上可能是 GBK) -
InputStreamReader不缓冲,BufferedReader才负责按行缓存 —— 两者要配对用,缺一不可 - 如果响应头里声明了
Content-Type: text/plain; charset=ISO-8859-1,就得改成对应编码,否则乱码
示例片段:
InputStream is = connection.getInputStream();
InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(isr);
StringBuilder content = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
content.append(line).append("\n");
}
br.close(); // 注意:close() 会级联关闭 isr 和 is
二进制或不确定编码时,用 ByteArrayOutputStream 累积字节
图片、PDF、ZIP、API 响应体(尤其是没明确 Content-Type 的)必须按原始字节处理。这时候字符流是错的入口。
核心逻辑:反复调用 is.read(buffer),把每次返回的有效长度 len 写入 ByteArrayOutputStream,直到返回 -1。
- 缓冲区大小建议设为 4096 或 8192,太小频繁系统调用,太大浪费内存
- 不要用
String result += line拼接 —— 字节流里没有“行”的概念,+=在循环里会触发大量临时对象 -
ByteArrayOutputStream的toByteArray()返回的是当前所有已写入字节的拷贝,安全,可直接用于后续解析
精简可靠写法:
ByteArrayOutputStream bos = new ByteArrayOutputStream(8192);
byte[] buf = new byte[4096];
int len;
while ((len = is.read(buf)) != -1) {
bos.write(buf, 0, len);
}
byte[] rawBytes = bos.toByteArray();
bos.close();
Android 里还要额外关连接,不然容易内存泄漏
在 Android 上,HttpURLConnection 或 OkHttp 的 Response.body().byteStream() 返回的 InputStream,背后绑着网络连接和 socket。只关流不关连接,连接池里的 socket 可能一直挂着。
- 如果是
HttpURLConnection,务必在 finally 块里调用connection.disconnect() - OkHttp 的
Response必须response.close(),它内部会释放流和连接 - 别在子线程里只关
InputStream就完事 —— 这是最常被忽略的资源泄漏点
尤其注意:Android 7.0+ 对未关闭的连接有更严格的超时回收策略,漏关可能引发 SocketTimeoutException 或后续请求失败。











