You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 16更新后调用URL.openStream()抛出java.io.FileNotFoundException问题求助

Android 16更新后调用URL.openStream()抛出java.io.FileNotFoundException问题求助

看起来你遇到的是Android 16系统更新后网络请求的兼容性问题,我之前也碰到过类似的系统版本迭代导致的网络异常,结合你的日志和场景,给你梳理几个大概率的原因和排查方向,应该能帮你定位问题:

1. 先搞清楚:这大概率是“假”的FileNotFoundException

这个坑我踩过好几次!HttpURLConnection有个反直觉的设计:当请求返回4xx(比如403、404)或5xx的错误状态码时,调用getInputStream()会直接抛出FileNotFoundException,而不是告诉你真实的响应码。Android 16的网络行为变化可能导致你的请求触发了服务端的错误响应,比如权限拒绝、参数解析失败等。

排查方法:在调用openStream()之前,先主动获取响应码和错误流,看服务端到底返回了什么:

HttpURLConnection connection = (HttpURLConnection) downloadURL.openConnection();
// 先获取响应码,不要直接开流
int responseCode = connection.getResponseCode();
Log.d("DownloadDebug", "服务端响应码: " + responseCode);

if (responseCode == HttpURLConnection.HTTP_OK) {
    // 正常情况再获取输入流
    DataInputStream dataInputStream = new DataInputStream(connection.getInputStream());
} else {
    // 读取错误流,看服务端返回的具体错误信息
    InputStream errorStream = connection.getErrorStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream));
    StringBuilder errorMsg = new StringBuilder();
    String line;
    while ((line = reader.readLine()) != null) {
        errorMsg.append(line);
    }
    Log.e("DownloadDebug", "服务端错误信息: " + errorMsg.toString());
}

拿到真实的响应码和错误信息,基本就能解决一半问题了——比如如果是403,那就是服务端拒绝了你的请求;如果是400,可能是参数有问题。

2. Android 16的TLS安全策略收紧了

Android每代都会更新默认的TLS配置,比如Android 16可能彻底禁用了TLS 1.0/1.1这类旧版本协议,而你的服务端可能还在依赖这些旧协议,或者服务端的证书链有问题,导致请求被系统安全策略拦截。

排查&解决:

  • 先确认服务端是否支持TLS 1.2及以上版本,也可以直接在代码里显式指定TLS版本试试:
if (connection instanceof HttpsURLConnection) {
    HttpsURLConnection httpsConn = (HttpsURLConnection) connection;
    // 强制使用TLS 1.2
    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    sslContext.init(null, null, new SecureRandom());
    httpsConn.setSSLSocketFactory(sslContext.getSocketFactory());
}
  • 另外,也可能是服务端的证书在Android 16的信任列表里不被认可,你可以临时禁用证书校验(仅用于排查,上线绝对不能用),如果禁用后能成功,那就是证书的问题,需要服务端更新证书链:
// 仅排查用的信任所有证书的实现
httpsConn.setSSLSocketFactory(new TrustAllSSLSocketFactory());
httpsConn.setHostnameVerifier((hostname, session) -> true);

(TrustAllSSLSocketFactory的实现网上一搜就有,这里就不贴了)

3. 检查你的URL参数编码是否有问题

看你日志里的URL,参数之间用的是&(这是HTML实体的转义字符),但正常的URL参数分隔应该用&。虽然有些旧版本的Android会自动处理这种转义,但Android 16的URL解析可能更严格,导致服务端无法正确解析参数,返回404或400错误。

排查方法:把URL里的&全部替换成&,比如原URL里的:
https://myurl.com/android/index.php?route=data/download&param1=myparam1
改成:
https://myurl.com/android/index.php?route=data/download&param1=myparam1
然后再测试,看是否能正常请求。

另外,最好用URLEncoder来正确编码参数值,而不是直接拼接字符串,避免特殊字符导致的解析问题。

4. Android 16的后台网络访问限制

你的请求是在DataRefreshService这个后台服务里发起的,Android 16可能对后台服务的网络访问有了更严格的限制(比如默认禁止后台网络,或者需要额外的权限)。

排查方法:

  • 先试试在前台Activity里直接调用downloadData()方法,如果前台能成功,那就是后台网络限制的问题。
  • 检查Manifest里的权限是否齐全:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  • 如果是后台限制的问题,需要适配Android 16的后台策略,比如改用WorkManager来调度网络请求,或者在发起请求时临时申请前台服务权限(显示一个通知,让系统认为你的服务是前台服务)。

最后给个优先级建议

先优先排查响应码,这是最直接能定位问题的方法——毕竟FileNotFoundException很多时候只是表象,真正的原因在服务端的响应里。如果响应码是200但还是抛异常,再去排查TLS、URL编码或后台限制的问题。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 13:28:03