一、Http 請求及相應
1.1 請求包結構
示例:
POST /meme.php/home/user/login HTTP/1.1
Host: 114.215.86.90
Cache-Control: no-cache
Postman-Token: bd243d6b-da03-902f-0a2c-8e9377f6f6ed
Content-Type: application/x-www-form-urlencoded
tel=13637829200&password=123456
1.2 響應包結構
示例:
HTTP/1.1 200 OK
Date: Sat, 02 Jan 2016 13:20:55 GMT
Server: Apache/2.4.6 (CentOS) PHP/5.6.14
X-Powered-By: PHP/5.6.14
Content-Length: 78
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/json; charset=utf-8
{"status":202,"info":"\u6b64\u7528\u6237\u4e0d\u5b58\u5728\uff01","data":null}
二、Http 請求方式
2.1 GET
請求指定url的數據,請求體爲空(例如打開網頁)。
2.2 POST
請求指定url的數據,同時傳遞參數(在請求體中)。
2.3 HEAD
類似於get請求,只不過返回的響應體爲空,用於獲取響應頭。
2.4 PUT
從客戶端向服務器傳送的數據取代指定的文檔的內容。
2.5 DELETE
請求服務器刪除指定的頁面。
2.6 CONNECT
HTTP/1.1協議中預留給能夠將連接改爲管道方式的代理服務器。
2.7 OPTIONS
允許客戶端查看服務器的性能。
2.8 TRACE
回顯服務器收到的請求,主要用於測試或診斷。
其中,常用請求方式 GET 和 POST ,接下里就一起來熟悉下這兩種常用請求方式。
三、GET & POST
- GET 通常是從服務器獲取數據,POST 通常是香服務器傳輸數據。
- GET 是把參數數據隊列加到表單的 ACTION 屬性所指的 URL 中,值和表單內各個字段一一對應,在 URL 中可以看到,實際上就是 URL 拼接方式。POST 是通過 HTTP POST 機制,將表單內各個字段與其內容放置在 HTML HEADER 中一起傳送到 ACTION 屬性所指向的 URL 中。
- 服務器通過 Request.QueryString 獲取 GET 變量值,通過 Request.Form 獲取 POST 提交的值。
- GET 提交的數據量小,不能大於 1kb。POST 提交的數據量較大,一般被默認爲不受限制,但理論上,IIS4 中最大量爲 80KB,IIS5 中爲 100KB。
- GET 安全性低,POST 安全性高。
四、使用 HttpURLConnection 發送 HTTP 請求
以前,Android 上發送 HTTP 請求有兩種方式,HttpURLConnection 和 HttpClient。不過由於 HttpClient 存在 API 數量衆多,維護困難等缺點,Android 大版本6.0之後,HttpClient 就被完全移除了。現在官方建議使用 HttpURLConnection。
4.1 流程
4.1.1 獲取 HttpURLConnection 實例
new 一個 URL 對象,並傳入要訪問的地址,然後調用一下 HttpURLConnection 的 openConnection() 方法即可。
URL lURL = new URL("https://www.baidu.com/");//請求 url
lHttpURLConnection = (HttpURLConnection) lURL.openConnection();
4.1.2 設置 HTTP 請求方法
lHttpURLConnection.setRequestMethod("GET");//請求方法, HttpURLConnection 默認請求方式即爲 GET 請求,所以本句可省略
4.1.3 接下來可自由定製
如連接超時、讀取超時的設置,或服務器希望得到的一些消息頭等
lHttpURLConnection.setConnectTimeout(8000);//與服務器建立連接超時設置
lHttpURLConnection.setReadTimeout(8000);//與服務器傳遞數據的超時設置
4.1.4 讀取返回的輸入流
然後調用 getInstreamInput() 方法獲取服務器返回的輸入流
對返回的輸入流進行讀取
InputStream lInputStream = lHttpURLConnection.getInputStream();
4.1.5 關閉 HTTP 連接
lHttpURLConnection.disconnect();
4.2 示例
4.2.1 佈局
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ginkwang.networktest">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
4.2.2 代碼
public class MainActivity extends AppCompatActivity {
private Button mBtnSendRequest;
private TextView mTvResponseTxt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mBtnSendRequest = (Button) findViewById(R.id.btn_send_request);
mBtnSendRequest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendRequestWithHttpURLConnection();
}
});
mTvResponseTxt = (TextView) findViewById(R.id.tv_response_txt);
}
private void sendRequestWithHttpURLConnection() {
//開啓線程發起請求
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection lHttpURLConnection = null;
BufferedReader lBufferedReader = null;
try {
URL lURL = new URL("https://www.baidu.com/");//請求 url
lHttpURLConnection = (HttpURLConnection) lURL.openConnection();
lHttpURLConnection.setRequestMethod("GET");//請求方法,HttpURLConnection 默認請求方式即爲 GET 請求,所以本句可省略
lHttpURLConnection.setConnectTimeout(8000);//與服務器建立連接超時設置
lHttpURLConnection.setReadTimeout(8000);//與服務器傳遞數據的超時設置
InputStream lInputStream = lHttpURLConnection.getInputStream();
//讀取獲取到的輸入流
lBufferedReader = new BufferedReader(new InputStreamReader(lInputStream));
StringBuilder lStringBuilder = new StringBuilder();
String line = "";
while ((line = lBufferedReader.readLine()) != null) {
lStringBuilder.append(line);
}
showResponse(lStringBuilder.toString());
} catch (Exception pE) {
pE.printStackTrace();
} finally {
if (lBufferedReader != null) {
try {
lBufferedReader.close();
} catch (IOException pE) {
pE.printStackTrace();
}
}
if (lHttpURLConnection != null) {
lHttpURLConnection.disconnect();
}
}
}
}).start();
}
private void showResponse(final String result) {
runOnUiThread(new Runnable() {
@Override
public void run() {
// 線程切回到主線程,進行 UI 更新操作
mTvResponseTxt.setText(result);
}
});
}
}
4.2.3 權限聲明
<uses-permission android:name="android.permission.INTERNET"/>
4.2.4 效果
4.3 HttpURLConnection 參數詳解
4.3.1 setRequestMethod
設置請求方法
4.3.2 setDoOutPut
設置是否向 HttpURLConnection 輸出,Get 請求用不到 lHttpURLConnection.getOutputStream(),因爲參數直接追加在地址後面,因此默認是false。而 Post 請求參數要放在 http 正文內,因此需要設爲 true。
4.3.3 setDoInPut
設置是否從 HttpUrlConnection 讀入,默認情況下是 true;指示應用程序要從 URL 連接讀取數據。
4.3.4 setConnectTimeout
與服務器建立連接超時設置
4.3.5 setReadTimeOut
與服務器傳遞數據的超時設置
4.3.6 setInstanceFollowRedirects
設置本次連接是否自動重定向
4.3.7 setRequestProperty
設置頭信息,如格式等,一般不設置,有默認設置
4.3.8 connect
不必要調用 connect 方法(調用也無妨),實際上只是建立了一個與服務器的 tcp 連接,並沒有實際發送 http 請求。無論是 post 還是 get,http請求實際上直到HttpURLConnection 的 getInputStream() 這個函數裏面才正式發送出去。
4.4 基於 HttpURLConnection 的 HTTP 請求封裝
直接上代碼
/**
* 網絡請求類
* Created by Wang-gk on 2017/5/2.
*/
public class NetworkHandler {
private static final String TAG = "NetworkHandler";
private static final int TIMEOUT = 15000;//超時設置
/**
* 請求參數格式轉換
* @param pMap 請求參數
* @return
*/
private static String prepareParam(Map<String, Object> pMap) {
StringBuffer lStringBuffer = new StringBuffer();
if (pMap.isEmpty()) {
return "";
} else {
for (String key : pMap.keySet()) {
String value = (String) pMap.get(key);
if (lStringBuffer.length() < 1) {
lStringBuffer.append(key).append("=").append(value);
} else {
lStringBuffer.append("&").append(key).append("=").append(value);
}
}
}
return lStringBuffer.toString();
}
/**
* Get 請求
* @param pUrl 請求地址
* @param pParamMap 請求參數
* @param pListener 接口回調
*/
public static void doGet(String pUrl, Map<String, Object> pParamMap, NetworkCallbackListener pListener) {
HttpURLConnection lHttpURLConnection = null;
BufferedReader lBufferedReader = null;
if (pParamMap != null && pParamMap.size() > 0) {
pUrl += prepareParam(pParamMap);
}
try {
URL lURL = new URL(pUrl);
lHttpURLConnection = (HttpURLConnection) lURL.openConnection();//初始化 HttpURLConnection
lHttpURLConnection.setRequestMethod("GET");//設置請求方法
lHttpURLConnection.setDoOutput(false);//設置是否向connection輸出, get請求用不到 lHttpURLConnection.getOutputStream(),因爲參數直接追加在地址後面,因此默認是false
lHttpURLConnection.setDoInput(true);// 設置是否從 HttpUrlConnection 讀入,默認情況下是 true;指示應用程序要從 URL 連接讀取數據。
lHttpURLConnection.setConnectTimeout(TIMEOUT);//與服務器建立連接超時設置
lHttpURLConnection.setReadTimeout(TIMEOUT);//與服務器傳遞數據的超時設置
lHttpURLConnection.setInstanceFollowRedirects(true);//設置本次連接是否自動重定向
lHttpURLConnection.setRequestProperty("Content-Type","text/html; charset=UTF-8");//設置頭信息,如格式等,一般不設置,有默認設置
lHttpURLConnection.connect();//不必要調用connect方法(調用也無妨),實際上只是建立了一個與服務器的tcp連接,並沒有實際發送http請求。無論是post還是get,http請求實際上直到HttpURLConnection的getInputStream()這個函數裏面才正式發送出去。
InputStream lInputStream = lHttpURLConnection.getInputStream();
//讀取獲取到的輸入流
lBufferedReader = new BufferedReader(new InputStreamReader(lInputStream));
StringBuilder lStringBuilder = new StringBuilder();
String line = "";
while ((line = lBufferedReader.readLine()) != null) {
lStringBuilder.append(line);
}
Log.i(TAG, "doGet: output <<< " + lStringBuilder.toString());
if (pListener != null) {
pListener.onFinish(lStringBuilder.toString());
}
} catch (Exception pE) {
if (pListener != null) {
pListener.onError(pE);
}
} finally {
if (lHttpURLConnection != null) {
lHttpURLConnection.disconnect();
}
if (lBufferedReader != null) {
try {
lBufferedReader.close();
} catch (IOException pE) {
pE.printStackTrace();
}
}
}
}
/**
* Post 請求
* @param pUrl 請求地址
* @param pParamMap 請求參數
* @param pListener 接口回調
*/
public static void doPost(String pUrl, Map<String, Object> pParamMap, NetworkCallbackListener pListener) {
HttpURLConnection lHttpURLConnection = null;
BufferedReader lBufferedReader = null;
try {
URL lURL = new URL(pUrl);
lHttpURLConnection = (HttpURLConnection) lURL.openConnection();//初始化 HttpURLConnection
lHttpURLConnection.setRequestMethod("POST");//設置請求方法
lHttpURLConnection.setDoOutput(true);//設置是否向connection輸出,因爲這個是 post 請求,參數要放在 http 正文內,因此需要設爲 true
lHttpURLConnection.setDoInput(true);// 設置是否從 HttpUrlConnection 讀入,默認情況下是 true;指示應用程序要從 URL 連接讀取數據。
lHttpURLConnection.setConnectTimeout(TIMEOUT);//與服務器建立連接超時設置
lHttpURLConnection.setReadTimeout(TIMEOUT);//與服務器傳遞數據的超時設置
lHttpURLConnection.setInstanceFollowRedirects(true);//設置本次連接是否自動重定向
lHttpURLConnection.setRequestProperty("Content-Type","text/html; charset=UTF-8");//設置頭信息,如格式等,一般不設置,有默認設置
lHttpURLConnection.connect();//不必要調用connect方法(調用也無妨),實際上只是建立了一個與服務器的tcp連接,並沒有實際發送http請求。無論是post還是get,http請求實際上直到HttpURLConnection的getInputStream()這個函數裏面才正式發送出去。
InputStream lInputStream = lHttpURLConnection.getInputStream();
//讀取獲取到的輸入流
lBufferedReader = new BufferedReader(new InputStreamReader(lInputStream));
StringBuilder lStringBuilder = new StringBuilder();
String line = "";
while ((line = lBufferedReader.readLine()) != null) {
lStringBuilder.append(line);
}
Log.i(TAG, "doPost: output <<< " + lStringBuilder.toString());
if (pListener != null) {
pListener.onFinish(lStringBuilder.toString());
}
} catch (Exception pE) {
if (pListener != null) {
pListener.onError(pE);
}
} finally {
if (lHttpURLConnection != null) {
lHttpURLConnection.disconnect();
}
if (lBufferedReader != null) {
try {
lBufferedReader.close();
} catch (IOException pE) {
pE.printStackTrace();
}
}
}
}
}
其中,網絡請求的回調操作也封裝在內。主要就是交易完成 onFinish() 和 onError() 兩個回調方法,在接口回調類 NetworkCallbackListener 中定義
/**
* 網絡處理接口
* Created by Wang-gk on 2017/5/4.
*/
public interface NetworkCallbackListener {
void onFinish(String pResponse);
void onError(Exception pE);
}
五、參考
郭霖 《第一行代碼》