Android HTTP请求方式:HttpURLConnection

在 Android 中,HttpURLConnection 是 Java 提供的原生 HTTP 客户端,用于执行网络请求。它适用于简单的 HTTP 请求场景,如 GET、POST 等。以下是使用 HttpURLConnection 进行 Android HTTP 请求的详细学习指南,包括请求方式、代码示例、注意事项和实践建议。


一、HttpURLConnection 概述

  • 定义HttpURLConnection 是 Java 标准库中的类,继承自 URLConnection,用于处理 HTTP/HTTPS 请求。
  • 特点
  • 支持 GET、POST、PUT、DELETE 等 HTTP 方法。
  • 可设置请求头、请求体、超时时间。
  • 支持处理响应数据和状态码。
  • 原生支持 HTTPS(通过 SSL/TLS)。
  • 适用场景:简单网络请求、学习 HTTP 基础、不依赖第三方库的场景。
  • 局限性
  • 代码较为冗长,异步处理复杂。
  • 缺少高级功能(如连接池、拦截器),推荐生产环境中使用 OkHttp 或 Retrofit。

二、HttpURLConnection 的核心使用步骤

以下是使用 HttpURLConnection 执行 HTTP 请求的基本步骤:

  1. 声明网络权限
    AndroidManifest.xml 中添加:
   <uses-permission android:name="android.permission.INTERNET" />
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  1. 创建 URL 对象
    指定目标服务器地址。
   URL url = new URL("https://api.example.com/data");
  1. 打开连接
    使用 openConnection() 创建 HttpURLConnection 实例。
   HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  1. 配置请求
    设置请求方法、请求头、超时时间等。
   conn.setRequestMethod("GET"); // 或 "POST"、"PUT"、"DELETE"
   conn.setConnectTimeout(5000); // 连接超时,单位毫秒
   conn.setReadTimeout(5000);    // 读取超时,单位毫秒
   conn.setRequestProperty("Content-Type", "application/json");
  1. 发送请求(POST/PUT 需要)
    如果是 POST 或 PUT,写入请求体数据。
   conn.setDoOutput(true); // 允许输出
   OutputStream os = conn.getOutputStream();
   os.write("{\"key\":\"value\"}".getBytes());
   os.flush();
   os.close();
  1. 处理响应
    检查状态码,读取响应数据或处理错误。
   int responseCode = conn.getResponseCode();
   if (responseCode == HttpURLConnection.HTTP_OK) {
       InputStream in = conn.getInputStream();
       // 读取数据
   } else {
       InputStream err = conn.getErrorStream();
       // 处理错误
   }
  1. 关闭连接
    释放资源。
   conn.disconnect();

三、常见 HTTP 请求方式示例

以下是使用 HttpURLConnection 实现 GET 和 POST 请求的详细代码示例。

1. GET 请求

GET 请求用于从服务器获取资源,通常不包含请求体。

public String doGetRequest(String urlString) {
    HttpURLConnection conn = null;
    StringBuilder response = new StringBuilder();
    try {
        // 1. 创建 URL 和连接
        URL url = new URL(urlString);
        conn = (HttpURLConnection) url.openConnection();

        // 2. 配置请求
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(5000);
        conn.setReadTimeout(5000);
        conn.setRequestProperty("Accept", "application/json");
        // 添加认证头(可选)
        conn.setRequestProperty("Authorization", "Bearer your_token_here");

        // 3. 获取响应
        int responseCode = conn.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
            return response.toString();
        } else {
            // 处理错误
            return "Error: " + responseCode;
        }
    } catch (IOException e) {
        e.printStackTrace();
        return "Exception: " + e.getMessage();
    } finally {
        // 4. 关闭连接
        if (conn != null) {
            conn.disconnect();
        }
    }
}
  • 使用示例(在子线程或 AsyncTask 中调用):
  new Thread(() -> {
      String result = doGetRequest("https://api.example.com/data");
      // 在主线程更新 UI
      runOnUiThread(() -> {
          Log.d("Response", result);
      });
  }).start();

2. POST 请求

POST 请求用于向服务器提交数据,包含请求体。

public String doPostRequest(String urlString, String jsonBody) {
    HttpURLConnection conn = null;
    StringBuilder response = new StringBuilder();
    try {
        // 1. 创建 URL 和连接
        URL url = new URL(urlString);
        conn = (HttpURLConnection) url.openConnection();

        // 2. 配置请求
        conn.setRequestMethod("POST");
        conn.setConnectTimeout(5000);
        conn.setReadTimeout(5000);
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestProperty("Accept", "application/json");
        conn.setDoOutput(true); // 允许写入请求体

        // 3. 写入请求体
        OutputStream os = conn.getOutputStream();
        os.write(jsonBody.getBytes("UTF-8"));
        os.flush();
        os.close();

        // 4. 获取响应
        int responseCode = conn.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
            return response.toString();
        } else {
            BufferedReader errorReader = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
            String line;
            while ((line = errorReader.readLine()) != null) {
                response.append(line);
            }
            errorReader.close();
            return "Error: " + responseCode + " - " + response.toString();
        }
    } catch (IOException e) {
        e.printStackTrace();
        return "Exception: " + e.getMessage();
    } finally {
        // 5. 关闭连接
        if (conn != null) {
            conn.disconnect();
        }
    }
}
  • 使用示例(在子线程中调用):
  new Thread(() -> {
      String jsonBody = "{\"username\":\"user\",\"password\":\"pass\"}";
      String result = doPostRequest("https://api.example.com/login", jsonBody);
      // 在主线程更新 UI
      runOnUiThread(() -> {
          Log.d("Response", result);
      });
  }).start();

3. 其他请求方法(PUT、DELETE)

PUT 和 DELETE 的实现与 POST 类似,只需修改 setRequestMethod

conn.setRequestMethod("PUT"); // 或 "DELETE"

四、处理响应头与错误

1. 读取响应头

Map<String, List<String>> headers = conn.getHeaderFields();
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
    Log.d("Header", entry.getKey() + ": " + entry.getValue());
}
// 获取特定头
String contentType = conn.getHeaderField("Content-Type");
String setCookie = conn.getHeaderField("Set-Cookie");

2. 处理错误

  • 检查状态码:
  int responseCode = conn.getResponseCode();
  if (responseCode >= 400) {
      InputStream errorStream = conn.getErrorStream();
      BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream));
      StringBuilder error = new StringBuilder();
      String line;
      while ((line = reader.readLine()) != null) {
          error.append(line);
      }
      reader.close();
      Log.e("Error", "Code: " + responseCode + ", Message: " + error.toString());
  }

3. 处理 HTTPS

HttpURLConnection 默认支持 HTTPS。如果遇到自签名证书问题,需自定义 TrustManager(生产环境谨慎使用):

// 示例:信任所有证书(仅用于调试)
TrustManager[] trustAllCerts = new TrustManager[]{
    new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() { return null; }
        public void checkClientTrusted(X509Certificate[] certs, String authType) {}
        public void checkServerTrusted(X509Certificate[] certs, String authType) {}
    }
};
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

五、异步处理

Android 不允许在主线程执行网络操作,需使用异步机制:

1. 使用 Thread

如上述示例,直接在子线程中执行请求。

2. 使用 AsyncTask(已废弃)

public class NetworkTask extends AsyncTask<String, Void, String> {
    @Override
    protected String doInBackground(String... params) {
        return doGetRequest(params[0]);
    }

    @Override
    protected void onPostExecute(String result) {
        Log.d("Response", result);
    }
}

3. 使用 Kotlin 协程(推荐)

suspend fun doGetRequest(urlString: String): String = withContext(Dispatchers.IO) {
    val url = URL(urlString)
    val conn = url.openConnection() as HttpURLConnection
    try {
        conn.requestMethod = "GET"
        conn.setRequestProperty("Accept", "application/json")
        val responseCode = conn.responseCode
        if (responseCode == HttpURLConnection.HTTP_OK) {
            conn.inputStream.bufferedReader().use { it.readText() }
        } else {
            "Error: $responseCode"
        }
    } finally {
        conn.disconnect()
    }
}

// 调用
lifecycleScope.launch {
    val result = doGetRequest("https://api.example.com/data")
    Log.d("Response", result)
}

六、注意事项

  1. 子线程执行
  • 网络操作必须在子线程运行,否则抛出 NetworkOnMainThreadException
  1. 资源释放
  • 始终调用 conn.disconnect() 释放连接。
  • 关闭 InputStreamOutputStreamReader
  1. 错误处理
  • 处理 IOException、超时、状态码错误。
  • 检查网络状态:
    java ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = cm.getActiveNetworkInfo(); if (networkInfo == null || !networkInfo.isConnected()) { // 无网络 }
  1. HTTPS 安全
  • 生产环境中避免信任所有证书,使用证书固定(Certificate Pinning)。
  1. 性能优化
  • 设置合理的超时时间(setConnectTimeoutsetReadTimeout)。
  • 使用 Gzip 压缩(添加请求头 Accept-Encoding: gzip):
    java conn.setRequestProperty("Accept-Encoding", "gzip"); InputStream in = conn.getInputStream(); if ("gzip".equals(conn.getContentEncoding())) { in = new GZIPInputStream(in); }

七、学习建议与实践

  1. 学习路径
  • 理解 HTTP 协议基础(请求/响应、方法、状态码、头)。
  • 掌握 HttpURLConnection 的基本使用(GET、POST)。
  • 学习异步处理(Thread、AsyncTask、协程)。
  • 深入响应头处理(Cookie、缓存)。
  • 对比 OkHttp 和 Retrofit,了解 HttpURLConnection 的局限性。
  1. 实践项目
  • 调用公开 API(如 OpenWeatherMap),实现 GET 请求获取天气数据。
  • 实现登录功能,使用 POST 请求发送 JSON 数据。
  • 处理 Cookie,保存 Set-Cookie 并在后续请求中发送。
  1. 调试工具
  • 使用 Postman 测试 API,验证请求/响应。
  • 使用 Charles 或 Android Studio 的 Network Profiler 分析请求。
  1. 推荐资源
  • 官方文档:https://developer.android.com/reference/java/net/HttpURLConnection
  • MDN HTTP 文档:https://developer.mozilla.org/en-US/docs/Web/HTTP
  • 实践 API:https://api.github.com/ 或 https://openweathermap.org/api

八、总结

  • HttpURLConnection 是 Android 原生的 HTTP 客户端,适合简单请求。
  • 核心步骤:创建 URL、配置连接、设置请求头/体、处理响应、释放资源。
  • 请求方式
  • GET:获取数据,设置请求头(如 Accept)。
  • POST:提交数据,写入请求体(如 JSON)。
  • 注意事项:异步处理、资源释放、错误处理、HTTPS 安全。
  • 进阶建议:学习 OkHttp 和 Retrofit 以替代 HttpURLConnection,实现更高效的网络编程。

如果需要更详细的代码示例(如文件上传、处理特定头)或针对某个场景的深入讲解,请告诉我!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注