Android 主流開源框架(一)OkHttp 鋪墊-HttpClient 與 HttpURLConnection 使用詳解

文章首發於個人我的博客:wildma的博客,這裏有更好的閱讀體驗,歡迎關注。css

前言

最近有個想法——就是把 Android 主流開源框架進行深刻分析,而後寫成一系列文章,包括該框架的詳細使用與源碼解析。目的是經過鑑賞大神的源碼來了解框架底層的原理,也就是作到不只要知其然,還要知其因此然。html

這裏我說下本身閱讀源碼的經驗,我通常都是按照平時使用某個框架或者某個系統源碼的使用流程入手的,首先要知道怎麼使用,而後再去深究每一步底層作了什麼,用了哪些好的設計模式,爲何要這麼設計。java

系列文章:android

更多幹貨請關注 AndroidNotesgit

1、HttpClient 與 HttpURLConnection 介紹

1.1 HttpClient

HttpClient 是 Apache 提供的開源庫,能夠用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包。本來 Android SDK 中是包含了 HttpClient 的,可是 Google 在 Android 6.0 版本已經刪除了該庫的相關代碼,只能經過依賴庫的方式進行使用。github

1.2 HttpURLConnection

HttpURLConnection 是 java.net 包中提供用來訪問 HTTP 協議的基本功能的類,它繼承自 URLConnection,可用於向指定網站發送 GET 請求、POST 請求。HttpURLConnection 相對於 HttpClient 沒什麼封裝,但也是由於這個咱們能夠更容易的去擴展它。apache

不過在 Android 2.2 版本以前,HttpURLConnection 一直存在着一些使人厭煩的 bug。好比說對一個可讀的 InputStream 調用 close() 方法時,就有可能會致使鏈接池失效了。那麼咱們一般的解決辦法就是直接禁用掉鏈接池的功能,以下:編程

private void disableConnectionReuseIfNecessary() {
      // 這是一個2.2版本以前的bug
      if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
            System.setProperty("http.keepAlive", "false");
      }
}
複製代碼

2、HttpClient 的使用

2.1 使用前準備

  1. 加入網絡權限
<uses-permission android:name="android.permission.INTERNET"/>
複製代碼
  1. 若是使用 Android Studio,則須要在使用的 module 下的 build.gradle 中加入以下:
android {
    useLibrary 'org.apache.http.legacy'
}
複製代碼
  1. 從 Android 9 開始,默認狀況下該庫已從 bootclasspath 中移除且不可用於應用。 要繼續使用 Apache HTTP 客戶端,以 Android 9 及更高版本爲目標的應用能夠向其 AndroidManifest.xml 的 application 節點下添加如下內容(具體可看-Apache HTTP 客戶端棄用):
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
複製代碼

2.2 HttpClient 的 GET 請求

使用流程已在註釋中詳細描述,具體代碼以下:json

private void getRequestByHttpClient() {
        //1. 建立 HttpClient 對象(DefaultHttpClient 爲 HttpClient 的實現類)
        HttpClient httpClient = new DefaultHttpClient();
        //2. 建立請求對象(這裏是 GET 請求),參數爲請求地址
        HttpGet httpGet = new HttpGet("https://www.baidu.com");
        try {
            //3.調用 HttpClient 對象的 execute 方法發送請求,返回 HttpResponse
            HttpResponse httpResponse = httpClient.execute(httpGet);
            //4. 檢查是否請求成功,狀態碼 200 表示成功
            if (httpResponse.getStatusLine().getStatusCode() == 200) {
                //5. 調用 HttpEntity 對象的 getContent 方法獲取響應數據的輸入流
                InputStream inputStream = httpResponse.getEntity().getContent();
                //6. 將輸入流轉換成字符串
                String data = inputStream2String(inputStream);
                Log.i(TAG, "data: " + data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
複製代碼

其中,inputStream2String 方法以下:設計模式

private String inputStream2String(InputStream inputStream) {
        String data = "";
        BufferedReader bufferedReader = null;
        try {
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            bufferedReader = new BufferedReader(inputStreamReader);
            StringBuilder stringBuilder = new StringBuilder();
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line).append("\n");
            }
            data = stringBuilder.toString();
            if (!TextUtils.isEmpty(data)) {
                //去除最後一個多餘的換行符
                data = data.substring(0, data.length() - 1);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return data;
    }
複製代碼

接下來直接調用就能夠了,須要注意的是網絡請求須要在子線程中調用,以下:

new Thread(new Runnable() {
            @Override
            public void run() {
                getRequestByHttpClient();
            }
        }).start();
複製代碼

打印結果以下:

<!DOCTYPE html>
    <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css>
<title>百度一下,你就知道</title>
</head> <body link=#0000cc> 省略部分日誌...</body> 
</html>
複製代碼

2.3 HttpClient 的 POST 請求

POST 請求與 GET 請求相似,不一樣的是須要將請求對象 HttpGet 換成 HttpPost,而後經過 HttpPost 的 setEntity 方法設置請求參數。使用流程已在註釋中詳細描述,具體代碼以下:

private void postRequestByHttpClient() {
        //1. 建立 HttpClient 對象(DefaultHttpClient 爲 HttpClient 的實現類)
        HttpClient httpClient = new DefaultHttpClient();
        //2. 建立請求對象(這裏是 POST 請求),參數爲請求地址(這裏用的是 postman 提供的測試地址)
        HttpPost httpPost = new HttpPost("https://postman-echo.com/post");
        try {
            //3. 調用 HttpPost 對象的 setEntity 方法設置須要的參數
            List<NameValuePair> postParams = new ArrayList<>();
            postParams.add(new BasicNameValuePair("username", "wildma"));
            postParams.add(new BasicNameValuePair("password", "123456"));
            httpPost.setEntity(new UrlEncodedFormEntity(postParams));

            //4.調用 HttpClient 對象的 execute 方法發送請求,返回 HttpResponse
            HttpResponse httpResponse = httpClient.execute(httpPost);
            //5. 檢查是否請求成功,狀態碼 200 表示成功
            if (httpResponse.getStatusLine().getStatusCode() == 200) {
                //6. 調用 HttpEntity 對象的 getContent 方法獲取響應數據的輸入流
                InputStream inputStream = httpResponse.getEntity().getContent();
                //7. 將輸入流轉換成字符串
                String data = inputStream2String(inputStream);
                Log.i(TAG, "data: " + data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
複製代碼

在子線程中調用該方法,打印結果以下:

{
 "args": {},
 "data": "",
 "files": {},
 "form": {
  "username": "wildma",
  "password": "123456"
 },
 "headers": {
  "x-forwarded-proto": "https",
  "host": "postman-echo.com",
  "content-length": "31",
  "content-type": "application/x-www-form-urlencoded",
  "user-agent": "Apache-HttpClient/UNAVAILABLE (java 1.4)",
  "x-forwarded-port": "443"
 },
 "json": {
  "username": "wildma",
  "password": "123456"
 },
 "url": "https://postman-echo.com/post"
}
複製代碼

3、HttpURLConnection 的使用

3.1 使用前準備

加入網絡權限

<uses-permission android:name="android.permission.INTERNET"/>
複製代碼

3.2 HttpURLConnection 的 GET 請求

使用流程已在註釋中詳細描述,具體代碼以下:

private void getRequestByHttpURLConnection() {
        try {
            //1. 建立 URL 對象
            URL url = new URL("https://www.baidu.com");
            //2. 調用 URL 對象的 openConnection 方法獲取 HttpURLConnection 實例
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            //3. 設置請求方法(這裏是 GET 請求)
            httpURLConnection.setRequestMethod("GET");
            //4. 鏈接
            httpURLConnection.connect();
            //5. 檢查是否請求成功,狀態碼 200 表示成功
            if (httpURLConnection.getResponseCode() == 200) {
                //6. 調用 HttpURLConnection 對象的 getInputStream 方法獲取響應數據的輸入流
                InputStream inputStream = httpURLConnection.getInputStream();
                //7. 將輸入流轉換成字符串
                String data = inputStream2String(inputStream);
                Log.i(TAG, "data: " + data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
複製代碼

在子線程中調用該方法,打印結果與 HttpClient 的 GET 請求同樣。

3.3 HttpURLConnection 的 POST 請求

POST 請求與 GET 請求相似,不一樣的是須要將請求方法 GET 換成 POST,而後經過輸出流設置請求參數,還須要設置容許輸入輸出,即 setDoInput(true) 與 setDoOutput(true)。使用流程已在註釋中詳細描述,具體代碼以下:

private void postRequestByHttpURLConnection() {
        try {
            //1. 建立 URL 對象
            URL url = new URL("https://postman-echo.com/post");
            //2. 調用 URL 對象的 openConnection 方法獲取 HttpURLConnection 實例
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            //3. 設置請求方法(這裏是 POST 請求)
            httpURLConnection.setRequestMethod("POST");
            /*4. 設置容許輸入輸出*/
            httpURLConnection.setDoInput(true);
            httpURLConnection.setDoOutput(true);
            /*5. 設置請求參數*/
            String params = new StringBuilder()
                    .append("username=" + URLEncoder.encode("wildma", "UTF-8"))
                    .append("&password=" + URLEncoder.encode("123456", "UTF-8")).toString();
            OutputStream outputStream = httpURLConnection.getOutputStream();
            outputStream.write(params.getBytes());
            outputStream.flush();
            outputStream.close();

            //6. 鏈接
            httpURLConnection.connect();
            //7. 檢查是否請求成功,狀態碼 200 表示成功
            if (httpURLConnection.getResponseCode() == 200) {
                //8. 調用 HttpURLConnection 對象的 getInputStream 方法獲取響應數據的輸入流
                InputStream inputStream = httpURLConnection.getInputStream();
                //9. 將輸入流轉換成字符串
                String data = inputStream2String(inputStream);
                Log.i(TAG, "data: " + data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
複製代碼

在子線程中調用該方法,打印結果與 HttpClient 的 POST 請求同樣。

4、HttpClient 與 HttpURLConnection 如何選擇?

在 Android 2.2 版本以前,HttpClient 擁有較少的 bug,所以使用它是最好的選擇。

而在 Android 2.3 版本及之後,HttpURLConnection 則是最佳的選擇。它的 API 簡單,體積較小,於是很是適用於Android 項目。壓縮和緩存機制能夠有效地減小網絡訪問的流量,在提高速度和省電方面也起到了較大的做用。

其實參考 Google 的網絡框架 Volley 也能得出結論,Volley 源碼中在 Android 2.3 及以上版本,使用的是 HttpURLConnection,而在 Android 2.2 及如下版本,使用的是 HttpClient。

5、源碼

HttpClient 與 HttpURLConnection 的使用 demo

相關文章
相關標籤/搜索