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

REST API在Java中正常运行,Android端却报FileNotFoundException问题排查

解决Android调用MySportsFeeds API出现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();
}

如果这段代码能解决问题,说明是证书验证的问题,生产环境需要正确导入服务器的证书到应用中。

总结

最可能的几个原因按优先级排序:

  1. 服务器返回了非200的状态码(比如401未授权),导致Android的HttpsURLConnection抛出FileNotFoundException
  2. 使用了Android低版本不支持的java.util.Base64类,导致编码失败
  3. 网络请求在主线程执行,触发了NetworkOnMainThreadException

按照上面的步骤一步步排查,先获取服务器的真实响应信息,就能快速定位到具体问题。

内容的提问来源于stack exchange,提问作者Adam Lee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:06:46