文章首發於個人我的博客:wildma的博客,這裏有更好的閱讀體驗,歡迎關注。css
最近有個想法——就是把 Android 主流開源框架進行深刻分析,而後寫成一系列文章,包括該框架的詳細使用與源碼解析。目的是經過鑑賞大神的源碼來了解框架底層的原理,也就是作到不只要知其然,還要知其因此然。html
這裏我說下本身閱讀源碼的經驗,我通常都是按照平時使用某個框架或者某個系統源碼的使用流程入手的,首先要知道怎麼使用,而後再去深究每一步底層作了什麼,用了哪些好的設計模式,爲何要這麼設計。java
系列文章:android
更多幹貨請關注 AndroidNotesgit
HttpClient 是 Apache 提供的開源庫,能夠用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包。本來 Android SDK 中是包含了 HttpClient 的,可是 Google 在 Android 6.0 版本已經刪除了該庫的相關代碼,只能經過依賴庫的方式進行使用。github
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");
}
}
複製代碼
<uses-permission android:name="android.permission.INTERNET"/>
複製代碼
android {
useLibrary 'org.apache.http.legacy'
}
複製代碼
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
複製代碼
使用流程已在註釋中詳細描述,具體代碼以下: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>
複製代碼
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"
}
複製代碼
加入網絡權限
<uses-permission android:name="android.permission.INTERNET"/>
複製代碼
使用流程已在註釋中詳細描述,具體代碼以下:
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 請求同樣。
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 請求同樣。
在 Android 2.2 版本以前,HttpClient 擁有較少的 bug,所以使用它是最好的選擇。
而在 Android 2.3 版本及之後,HttpURLConnection 則是最佳的選擇。它的 API 簡單,體積較小,於是很是適用於Android 項目。壓縮和緩存機制能夠有效地減小網絡訪問的流量,在提高速度和省電方面也起到了較大的做用。
其實參考 Google 的網絡框架 Volley 也能得出結論,Volley 源碼中在 Android 2.3 及以上版本,使用的是 HttpURLConnection,而在 Android 2.2 及如下版本,使用的是 HttpClient。