首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android 發送 POST 請求的通用 HttpClient 封裝(含超時、參數編碼、異常處理)

     編輯:程式設計綜合問答

為什麼要二次封裝

  • 原生 HttpURLConnection 代碼冗長,異常處理分散。
  • 項目中常需統一:連接/讀取超時、請求頭、參數編碼、日誌、重試策略。
  • 封裝後業務代碼只關心「URL、參數、回調」。

完整工具類(基於 HttpURLConnection,無第三方依賴)

public class HttpClientUtil {
    private static final int DEFAULT_CONN_TIMEOUT = 10_000; // 10s
    private static final int DEFAULT_READ_TIMEOUT = 10_000;
    private static final String DEFAULT_CHARSET = "UTF-8";

    /** 同步 POST,返回響應體字符串,非 2xx 拋異常 */
    public static String post(String url, Map params) throws IOException {
        return post(url, params, DEFAULT_CONN_TIMEOUT, DEFAULT_READ_TIMEOUT);
    }

    /** 同步 POST,自定義超時 */
    public static String post(String url, Map params, int connTimeout, int readTimeout) throws IOException {
        HttpURLConnection conn = null;
        try {
            URL u = new URL(url);
            conn = (HttpURLConnection) u.openConnection();
            conn.setRequestMethod("POST");
            conn.setConnectTimeout(connTimeout);
            conn.setReadTimeout(readTimeout);
            conn.setDoOutput(true);
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=" + DEFAULT_CHARSET);
            conn.setRequestProperty("Accept", "application/json");

            // 寫參數
            if (params != null && !params.isEmpty()) {
                StringBuilder sb = new StringBuilder();
                for (Map.Entry e : params.entrySet()) {
                    if (sb.length() > 0) sb.append('&');
                    sb.append(URLEncoder.encode(e.getKey(), DEFAULT_CHARSET))
                      .append('=')
                      .append(URLEncoder.encode(e.getValue(), DEFAULT_CHARSET));
                }
                try (OutputStream os = conn.getOutputStream()) {
                    os.write(sb.toString().getBytes(DEFAULT_CHARSET));
                }
            }

            int code = conn.getResponseCode();
            InputStream is = (code >= 200 && code < 300) ? conn.getInputStream() : conn.getErrorStream();
            try (BufferedReader br = new BufferedReader(new InputStreamReader(is, DEFAULT_CHARSET))) {
                StringBuilder resp = new StringBuilder();
                String line;
                while ((line = br.readLine()) != null) resp.append(line);
                if (code >= 400) throw new IOException("HTTP " + code + ": " + resp);
                return resp.toString();
            }
        } finally {
            if (conn != null) conn.disconnect();
        }
    }

    /** 異步 POST,回調在主線程(需配合 Handler/RunOnUiThread) */
    public static void postAsync(String url, Map params, Callback cb) {
        new Thread(() -> {
            try {
                String result = post(url, params);
                cb.onSuccess(result);
            } catch (IOException e) {
                cb.onFailure(e);
            }
        }).start();
    }

    public interface Callback {
        void onSuccess(String responseBody);
        void onFailure(IOException e);
    }
}

使用示例

// 在子線程/IntentService/RxJava/協程中調用
Map params = new HashMap<>();
params.put("userId", "123");
params.put("action", "login");
try {
    String json = HttpClientUtil.post("https://api.example.com/do", params);
    // 解析 json...
} catch (IOException e) {
    Log.e("HTTP", "請求失敗", e);
}

// 或異步
HttpClientUtil.postAsync(url, params, new HttpClientUtil.Callback() {
    @Override public void onSuccess(String body) { runOnUiThread(() -> parse(body)); }
    @Override public void onFailure(IOException e) { runOnUiThread(() -> toast("網絡錯誤")); }
});

進階擴展建議

  • 換用 OkHttp:連接池、透明 GZIP、HTTP/2、攔截器鏈、取消標籤,生產環境首選。
  • 統一錯誤碼映射:將 HTTP 狀態碼、業務 code 統一為 AppException 層級。
  • 請求/響應日誌攔截:OkHttp HttpLoggingInterceptor 或自定義 Filter。
  • 證書釘固/雙向認證:高安全場景配置 SSLSocketFactory。

避坑提醒

  1. **主線程網絡請求會拋 NetworkOnMainThreadException** —— 必須在子線程/異步任務中調用同步方法。
  2. **參數編碼必須雙端一致**(UTF-8),URLEncoder.encode 空格編為 +,符合 application/x-www-form-urlencoded 規範。
  3. **大文件上傳請用 Multipart**,別塞進表單字段。
  4. **關閉連接** —— conn.disconnect() 在 finally 塊,防連接洩漏。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved