REST API在Java中正常运行,Android端却报FileNotFoundException问题排查
我来帮你梳理下这个问题的排查方向——首先要明确:Android平台的HttpsURLConnection和桌面Java的实现存在差异,当服务器返回4xx/5xx这类错误状态码时,Android端会直接抛出FileNotFoundException,而不是像桌面Java那样返回错误流,这是你看到这个异常的核心原因。下面一步步来定位和解决问题:
第一步:先获取服务器的真实响应信息
你现在的代码直接调用getInputStream(),一旦服务器返回非200的状态码就会抛异常,根本看不到真实的错误原因。先修改代码,先获取响应码,再根据响应码选择获取输入流还是错误流:
try { URL url = new URL("https://api.mysportsfeeds.com/v2.1/pull/nfl/current/games.json"); String credentials = "[I_PUT_THE_CORRECT_API_KEY_HERE]:MYSPORTSFEEDS"; String encoding = java.util.Base64.getEncoder().encodeToString(credentials.getBytes()); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setRequestProperty("Authorization", "Basic " + encoding); // 先获取响应码,这是关键! int responseCode = connection.getResponseCode(); System.out.println("响应码:" + responseCode); InputStream content; if (responseCode == HttpsURLConnection.HTTP_OK) { content = connection.getInputStream(); } else { // 错误状态下获取错误流,查看服务器返回的具体错误 content = connection.getErrorStream(); } BufferedReader in = new BufferedReader(new InputStreamReader(content)); String line; StringBuilder response = new StringBuilder(); while ((line = in.readLine()) != null) { response.append(line); } System.out.println("服务器响应内容:" + response.toString()); } catch (Exception ex) { System.out.println("Exception: " + ex.toString()); ex.printStackTrace(); }
运行这段代码后,你就能看到服务器返回的真实状态码(比如401代表未授权,403代表禁止访问)和错误信息,这是定位问题的第一步。
第二步:排查Base64编码的兼容性
你用的java.util.Base64是Java 8引入的类,而Android在API 26(Android 8.0)之后才支持这个类。如果你的应用运行在低于API 26的设备上,这部分代码会抛出NoClassDefFoundError,间接导致网络请求失败,最终表现为FileNotFoundException。
解决办法是改用Android原生的android.util.Base64类,替换编码部分的代码:
// 替换原来的编码代码 String credentials = "[I_PUT_THE_CORRECT_API_KEY_HERE]:MYSPORTSFEEDS"; // 使用NO_WRAP参数避免编码后的字符串自动换行,符合HTTP Basic Auth的要求 String encoding = android.util.Base64.encodeToString(credentials.getBytes(), android.util.Base64.NO_WRAP);
第三步:确保网络请求不在主线程执行
Android从API 11开始,严格禁止在主线程执行网络请求,否则会抛出NetworkOnMainThreadException。虽然你当前的异常是FileNotFoundException,但如果你的代码是在Activity的onCreate、onResume等主线程方法里执行的,可能会触发这个异常,进而导致流程中断,表现为类似的错误。
解决办法是把网络请求放到异步线程中执行,比如用AsyncTask(适合简单场景):
private class FetchGamesTask extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... voids) { try { URL url = new URL("https://api.mysportsfeeds.com/v2.1/pull/nfl/current/games.json"); String credentials = "[I_PUT_THE_CORRECT_API_KEY_HERE]:MYSPORTSFEEDS"; String encoding = android.util.Base64.encodeToString(credentials.getBytes(), android.util.Base64.NO_WRAP); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setRequestProperty("Authorization", "Basic " + encoding); int responseCode = connection.getResponseCode(); InputStream content = responseCode == HttpsURLConnection.HTTP_OK ? connection.getInputStream() : connection.getErrorStream(); BufferedReader in = new BufferedReader(new InputStreamReader(content)); String line; StringBuilder response = new StringBuilder(); while ((line = in.readLine()) != null) { response.append(line); } return response.toString(); } catch (Exception ex) { ex.printStackTrace(); return "错误信息:" + ex.getMessage(); } } @Override protected void onPostExecute(String result) { // 在这里处理返回的结果,比如更新UI System.out.println("请求结果:" + result); } } // 在需要发起请求的地方调用 new FetchGamesTask().execute();
第四步:排查HTTPS证书兼容性
虽然你的桌面Java程序能正常运行,但Android的系统证书存储和桌面JVM不同,可能会导致无法验证服务器的HTTPS证书,进而请求失败。你可以先尝试添加自定义TrustManager来忽略证书验证(仅用于测试,生产环境不建议这么做),看看是否能解决问题:
// 在发起网络请求前执行这段代码,信任所有证书(测试用) try { TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {} public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {} } }; SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); // 忽略主机名验证 HostnameVerifier allHostsValid = (hostname, session) -> true; HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); } catch (Exception e) { e.printStackTrace(); }
如果这段代码能解决问题,说明是证书验证的问题,生产环境需要正确导入服务器的证书到应用中。
总结
最可能的几个原因按优先级排序:
- 服务器返回了非200的状态码(比如401未授权),导致Android的HttpsURLConnection抛出
FileNotFoundException - 使用了Android低版本不支持的
java.util.Base64类,导致编码失败 - 网络请求在主线程执行,触发了
NetworkOnMainThreadException
按照上面的步骤一步步排查,先获取服务器的真实响应信息,就能快速定位到具体问题。
内容的提问来源于stack exchange,提问作者Adam Lee

