大多數的Android應用程序都會使用HTTP協議來發送和接收網絡數據,Android中主要提供了兩種方式來進行HTTP操做,HttpURLConnection和HttpClient。因此首先介紹下Http協議。HTTP(HyperText Transfer Protocol,超文本傳輸協議) 是互聯網上應用最爲普遍的一種網絡協議。全部的WWW文件都必須遵照這個標準。HTTP協議採用了請求/響應模型。一般,由HTTP客戶端發起一個請求,創建一個到服務器指定端口(默認是80端口)的TCP鏈接,HTTP服務器則在那個端口監聽客戶端發送過來的請求。一旦收到請求,服務器(向客戶端)發回一個狀態行和響應消息,最後消息傳送完畢時,能夠關閉客戶端和服務器端的鏈接。java
Http消息由從客戶機到服務器的請求和從服務器到客戶機的響應構成。這兩種類型的消息均由一個起始行,一個或者多個頭域,一個指示頭域結束的空行和可選的消息體組成。HTTP的頭域包括通用頭,請求頭,響應頭和實體頭四個部分。android
請求消息格式以下:緩存
請求行 - 通用信息頭 - 請求頭 - 實體頭 - 消息主體服務器
以下所示:網絡
應答消息格式以下:異步
狀態行 - 通用信息頭 - 響應頭 - 實體頭 - 消息主體ide
以下所示:工具
注:Android HttpUrlConnection中只未提供Connect方法 post
方法性能 |
描 述 |
GET |
請求指定url的數據,請求體爲空(例如打開網頁) |
POST |
請求指定url的數據,同時傳遞參數(在請求體中) |
HEAD |
相似於get請求,只不過返回的響應體爲空,用於獲取響應頭 |
PUT |
從客戶端向服務器傳送的數據取代指定的文檔的內容 |
DELETE |
請求服務器刪除指定的頁面 |
CONNECT |
HTTP/1.1協議中預留給可以將鏈接改成管道方式的代理服務器 |
OPTIONS |
容許客戶端查看服務器的性能 |
TRACE |
回顯服務器收到的請求,主要用於測試或診斷。 |
狀態碼是一個用於標識服務器響應結果的代碼,由3位數字表示。狀態碼的第一個數字定義響應的類別,後兩個數字沒有分類的做用。(狀態碼後面會跟隨一段用於簡要描述狀態碼信息的文本)
狀態碼類型:
類型 |
類型描述 |
1xx |
信息響應類,表示接收到請求而且繼續處理 |
2xx |
處理成功響應類,表示動做被成功接收、理解和接受 |
3xx |
重定向響應類,爲了完成指定的動做,必須接受進一步處理 |
4xx |
客戶端錯誤,客戶請求包含語法錯誤或者是不能正確執行 |
5xx |
服務端錯誤,服務器不能正確執行一個正確的請求 |
常見狀態碼詳解:
狀態碼 |
描述 |
200 OK |
請求成功(其後是對GET和POST請求的應答文檔) |
301 Moved Permanently |
所請求的頁面已經轉移至新的url |
304 Not Modified |
服務器端的數據在If-modified-since後未發生修改 |
400 Bad Request |
服務器未能理解請求 |
401 Unauthorized |
被請求的頁面須要用戶名和密碼 |
404 Not Found |
服務器沒法找到被請求的頁面 |
408 Request Timeout |
請求超出了服務器的等待時間 |
500 Internal Server Error |
請求未完成。服務器遇到不可預知的狀況 |
501 Not Implemented |
請求未完成。服務器不支持所請求的功能 |
通用頭域
通用頭域包含請求和響應消息都支持的頭域,通用頭域包含Cache-Control、Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via.
1) Cache-Control:Cache-Control指定請求和響應遵循的緩存機制。請求時的緩存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached,響應消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age。
2) Date頭域:Date頭域表示消息發送的時間,如Date:Mon,31Dec200104:25:57GMT
請求頭域
請求頭域中包含請求消息特有的頭域信息,如Host、Range、User-Agent等
1) Host:Host頭域指定請求資源的Intenet主機和端口號;
2)Range:Range頭域能夠請求實體的一個或者多個子範圍,如bytes=500-600,601-9993);
3)User-Agent:User-Agent頭域的內容包含發出請求的用戶信息
響應頭域
響應頭域中包含響應消息中特有的頭域信息,如Location、Server等
1)Location:Location頭域用於重定向接收者到一個新URI地址;
2)Server:Server頭域包含處理請求的原始服務器的軟件信息;
實體頭域
請求消息和響應消息均可以包含實體信息,實體信息通常由實體頭域和實體組成。實體頭域包含關於實體的原信息,實體頭包括Allow、Content-Base、Content-Encoding、Content-Language、Content-Length、Content-Location、Content-MD五、Content-Range、Content-Type、Etag、Expires、Last-Modified、extension-header;
1)Content-Type:Content-Type實體頭用於向接收方指示實體的介質類型,指定HEAD方法送到接收方的實體介質類型,或GET方法發送的請求介質類型;
2)Content-Range:Content-Range實體頭用於指定整個實體中的一部分的插入位置,他也指示了整個實體的長度。主要用於對範圍請求的響應或對一系列範圍的重疊請求。
通常格式:Content-Range:bytes-unitSPfirst-byte-pos-last-byte-pos/entity-legth
如:Content-Range:bytes0-499/1234;
3)Last-modified:Last-modified實體頭指定服務器上保存內容的最後修訂時間
HttpClient和HttpURLConnection是Android中提供的兩種進行HTTP操做的方式,這兩種方式都支持HTTPS協議、以流的形式進行上傳和下載、配置超時時間、IPv六、以及鏈接池等功能。
DefaultHttpClient和AndroidHttpClient都是HttpClient的具體實現類,都擁有衆多的API,並且實現比較穩定,bug數量也不多。但同時也因爲HttpClient的API數量過多,使得很難在不破壞兼容性的狀況下對它進行升級和擴展,因此目前Android團隊在提高和優化HttpClient方面的工做態度並不積極;
而HttpURLConnection是一種多用途、輕量極的HTTP客戶端,使用它來進行HTTP操做能夠適用於大多數的應用程序,同時其升級與維護也相對比較簡單。但在Android 2.3版本以前,HttpURLConnection一直存在着一些使人厭煩的bug。好比說對一個可讀的InputStream調用close()方法時,就有可能會致使鏈接池失效。一般的解決辦法就是直接禁用掉鏈接池的功能。
注意:Google建議Android2.3及以上版本的應用使用HttpURLConnection,而Android2.3如下版本的應用使用HttpClient。緣由以下:
在Android 2.2版本以前,HttpClient擁有較少的bug,所以使用它是最好的選擇。
而在Android 2.3版本及之後,HttpURLConnection則是最佳的選擇。它的API簡單,體積較小,於是很是適用於Android項目。壓縮和緩存機制能夠有效地減小網絡訪問的流量,在提高速度和省電方面也起到了較大的做用。同時,Google Android團隊也在積極對該方式進行維護和升級。
/** * 採用HttpURLConnection進行網絡請求的工具類 */ public class AsyncNetworkUtil { private static String TAG = "AsyncNetworkUtil"; /** * 發送一個post請求 * @param url:請求的地址 * @param content:post請求參數 * @return: 返回服務器響應信息 */ public static String post(String url, String content) { HttpURLConnection conn = null; String response = null; try { // 建立一個URL對象 URL mURL = new URL(url); // 調用URL的openConnection()方法,獲取HttpURLConnection對象 conn = (HttpURLConnection) mURL.openConnection(); conn.setRequestMethod("POST");// 設置請求方法爲post conn.setReadTimeout(5000);// 設置讀取超時爲5秒 conn.setConnectTimeout(10000);// 設置鏈接網絡超時爲10秒 conn.setUseCaches(false); //設置不使用緩存 conn.setDoOutput(true);// 設置此方法,容許向服務器輸出內容 // post請求的參數 String data = content; // 得到一個輸出流,向服務器寫數據,默認狀況下,系統不容許向服務器輸出內容 OutputStream out = conn.getOutputStream(); out.write(data.getBytes()); out.flush(); out.close(); int responseCode = conn.getResponseCode();// 調用此方法就沒必要再使用conn.connect()方法 if (responseCode != -1) { InputStream is = conn.getInputStream(); response = getStringFromInputStream(is); } else { response = "No valid response code"; } } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { conn.disconnect();// 關閉鏈接 } } return response; } /** * 發送一個get請求 * @param url:請求的地址 * @return: 響應信息 */ public static String get(String url) { HttpURLConnection conn = null; try { // 利用string url構建URL對象 URL mURL = new URL(url); conn = (HttpURLConnection) mURL.openConnection(); conn.setRequestMethod("GET"); conn.setReadTimeout(5000); conn.setConnectTimeout(10000); conn.connect(); int responseCode = conn.getResponseCode(); if (responseCode == 200) { InputStream is = conn.getInputStream(); String response = getStringFromInputStream(is); return response; }else if ((responseCode + "") != null && (responseCode + "").length() != 0){ return responseCode + " "; } else { throw new NetworkErrorException("response status is "+responseCode); } } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { conn.disconnect(); } } return null; } /** * 從輸入流中獲取數據 * @param is * @return * @throws IOException */ private static String getStringFromInputStream(InputStream is) throws IOException { ByteArrayOutputStream os = new ByteArrayOutputStream(); // 模板代碼 必須熟練 byte[] buffer = new byte[1024]; int len = -1; while ((len = is.read(buffer)) != -1) { os.write(buffer, 0, len); } is.close(); String state = os.toString();// 把流中的數據轉換成字符串,採用的編碼是utf-8(模擬器默認編碼) os.close(); return state; } }
開啓網絡權限
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
檢查設備的網絡鏈接情況
/** * 這是一個工具類,用於檢測網絡鏈接情況 */ public class NetworkConnectivityUtil { private static String TAG = "NetworkConnectivityUtil"; //Log Tag private static ConnectivityManager connMgr; //網絡鏈接管理器對象 public NetworkConnectivityUtil(Context ctx){ connMgr = (ConnectivityManager) ctx.getSystemService(ctx.CONNECTIVITY_SERVICE); } /** * 檢查網絡鏈接情況 * @return true:網絡連通 * false:網絡未連通 */ public boolean checkNetwork() { NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); return networkInfo.isConnected() ? true : false; } /** * 檢查WIFI鏈接情況 * @return true:網絡連通 * false:網絡未連通 */ public boolean checkWifi() { NetworkInfo wifiInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); return wifiInfo.isConnected() ? true : false; } /** * 檢查手機網絡鏈接情況 * @return true:網絡連通 * false:網絡未連通 */ public boolean checkMobile() { NetworkInfo mobileInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); return mobileInfo.isConnected() ? true : false; } }
在工做線程中進行網絡操做
/** * 經過一個異步任務AsyncTask進行網絡操做 */ new AsyncTask<String, Void, String>() { @Override protected String doInBackground(String... params) { //NetworkUtil.get即上面HttpURLConnection中的方法 return NetworkUtil.get(params[0]); } @Override protected void onPostExecute(String s) { editText.setText(s.substring(0, 100)); } }.execute(url);