大多數Android的app都會使用HTTP協議來發送和接收數據。在Android開發中,一般使用兩種http客戶端:一個是Apache的HttpClient,另外一個是HttpURLConnection。這兩種HTTP客戶端API都支持HTTPS協議,流數據上傳和下載,配置超時,IPV6協議以及鏈接池等等。android
HttpClient的API衆多,而且bug少比較穩定。可是,HttpClient的API比較大,很難在保證兼容性的前提下去對其進行擴展。因此不少Android團隊並不太喜歡使用它。HttpClient是一個接口,裏面封裝了須要執行的http請求,身份驗證,鏈接管理等等,有三個主要的實現類:AbstractHttpClient, AndroidHttpClient, DefaultHttpClient。下面咱們來看一下AndroidHttpClient,它對DefaultHttpClient進行了改進使之更適合於Android開發。因而,AndroidHttpClient進行請求發送和響應接收步驟以下:服務器
一般狀況下,咱們並不在主線程中進行網絡請求操做,而是新開一個子線程來進行網絡操做,下面的代碼展現瞭如何利用AndroidHttpClient完成網絡登陸驗證的任務:網絡
public class LoginTask implements Runnable { private String username; private String password; public LoginTask(String username, String password) { // 初始化用戶名和密碼 this.username = username; this.password = password; } @Override public void run() { // 設置訪問的Web站點 String path = "http://xxxx/loginas.aspx"; //設置Http請求參數 Map<String, String> params = new HashMap<String, String>(); params.put("username", username); params.put("password", password); String result = sendHttpClientPost(path, params, "utf-8"); //把返回的接口輸出 System.out.println(result); } /** * 發送Http請求到Web站點 * @param path Web站點請求地址 * @param map Http請求參數 * @param encode 編碼格式 * @return Web站點響應的字符串 */ private String sendHttpClientPost(String path,Map<String, String> map,String encode) { List<NameValuePair> list=new ArrayList<NameValuePair>(); if(map!=null&&!map.isEmpty()) { for(Map.Entry<String, String> entry:map.entrySet()) { //解析Map傳遞的參數,使用一個鍵值對對象BasicNameValuePair保存。 list.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } } try { //實現將請求 的參數封裝封裝到HttpEntity中。 UrlEncodedFormEntity entity=new UrlEncodedFormEntity(list, encode); //使用HttpPost請求方式 HttpPost httpPost=new HttpPost(path); //設置請求參數到Form中。 httpPost.setEntity(entity); //實例化一個默認的Http客戶端,使用的是AndroidHttpClient HttpClient client=AndroidHttpClient.newInstance(""); //執行請求,並得到響應數據 HttpResponse httpResponse= client.execute(httpPost); //判斷是否請求成功,爲200時表示成功,其餘均問有問題。 if(httpResponse.getStatusLine().getStatusCode()==200) { //經過HttpEntity得到響應流 InputStream inputStream=httpResponse.getEntity().getContent(); return changeInputStream(inputStream,encode); } } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClientProtocolException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return ""; } /** * 把Web站點返回的響應流轉換爲字符串格式 * @param inputStream 響應流 * @param encode 編碼格式 * @return 轉換後的字符串 */ private String changeInputStream(InputStream inputStream, String encode) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] data = new byte[1024]; int len = 0; String result=""; if (inputStream != null) { try { while ((len = inputStream.read(data)) != -1) { outputStream.write(data,0,len); } result=new String(outputStream.toByteArray(),encode); } catch (IOException e) { e.printStackTrace(); } } return result; } }
相比之下,HttpURLConnection就顯得更通用一些。它是一款多用途,輕量級的HTTP客戶端,適用於大多數的app客戶端。同時,HttpURLConnection簡單,便於擴展。可是坑爹的是,在Android2.2以前,HttpURLConnection有一些bug,例如在試圖關閉InputStream的時候會致使鏈接池失效。因此,推薦在2.3以後的版本中使用HttpURLConnection。app
下面以請求百度首頁的logo爲例子,演示使用HttpURLConnection的GET方法完成網絡請求的任務:async
public class getImageTask{ private static String URL_PATH = http://www.baidu.com/img/bd_logo1.png; /** * @param args */ public static void main(String[] args) { // 調用方法獲取圖片並保存 saveImageToDisk(); } /** * 經過URL_PATH的地址訪問圖片並保存到本地 */ public static void saveImageToDisk() { InputStream inputStream= getInputStream(); byte[] data=new byte[1024]; int len=0; FileOutputStream fileOutputStream=null; try { //把圖片文件保存在本地F盤下 fileOutputStream=new FileOutputStream("F:\\test.png"); while((len=inputStream.read(data))!=-1) { //向本地文件中寫入圖片流 fileOutputStream.write(data,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //最後關閉流 if(inputStream!=null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if(fileOutputStream!=null) { try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 經過URL獲取圖片 * @return URL地址圖片的輸入流。 */ public static InputStream getInputStream() { InputStream inputStream = null; HttpURLConnection httpURLConnection = null; try { //根據URL地址實例化一個URL對象,用於建立HttpURLConnection對象。 URL url = new URL(URL_PATH); if (url != null) { //openConnection得到當前URL的鏈接 httpURLConnection = (HttpURLConnection) url.openConnection(); //設置3秒的響應超時 httpURLConnection.setConnectTimeout(3000); //設置容許輸入 httpURLConnection.setDoInput(true); //設置爲GET方式請求數據 httpURLConnection.setRequestMethod("GET"); //獲取鏈接響應碼,200爲成功,若是爲其餘,均表示有問題 int responseCode=httpURLConnection.getResponseCode(); if(responseCode==200) { //getInputStream獲取服務端返回的數據流。 inputStream=httpURLConnection.getInputStream(); } } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return inputStream; } }
若是使用POST方法,則須要設置請求參數。下面咱們使用POST方式,利用HttpURLConnection完成和上面同樣的登陸驗證功能:ide
public class LoginTask implements Runnable{ private static String PATH = "http://xxxxx/loginas.aspx"; private static URL url; private String username;
private String password;
public LoginTask(String username, String password) {
// 初始化用戶名和密碼
this.username = username;
this.password = password;
} /** * 經過給定的請求參數和編碼格式,獲取服務器返回的數據 * @param params 請求參數 * @param encode 編碼格式 * @return 得到的字符串 */ public static String sendPostMessage(Map<String, String> params, String encode) { StringBuffer buffer = new StringBuffer(); if (params != null && !params.isEmpty()) { for (Map.Entry<String, String> entry : params.entrySet()) { try { buffer.append(entry.getKey()) .append("=") .append(URLEncoder.encode(entry.getValue(), encode)) .append("&");//請求的參數之間使用&分割。 } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } buffer.deleteCharAt(buffer.length() - 1); System.out.println(buffer.toString()); try { HttpURLConnection urlConnection = (HttpURLConnection) url .openConnection(); urlConnection.setConnectTimeout(3000); //設置容許輸入輸出 urlConnection.setDoInput(true); urlConnection.setDoOutput(true); byte[] mydata = buffer.toString().getBytes(); //設置請求報文頭,設定請求數據類型 urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); //設置請求數據長度 urlConnection.setRequestProperty("Content-Length", String.valueOf(mydata.length)); //設置POST方式請求數據 urlConnection.setRequestMethod("POST"); OutputStream outputStream = urlConnection.getOutputStream(); outputStream.write(mydata); int responseCode = urlConnection.getResponseCode(); if (responseCode == 200) { return changeInputStream(urlConnection.getInputStream(), encode); } } catch (IOException e) { e.printStackTrace(); } } return ""; } /** * 把服務端返回的輸入流轉換成字符串格式 * @param inputStream 服務器返回的輸入流 * @param encode 編碼格式 * @return 解析後的字符串 */ private static String changeInputStream(InputStream inputStream, String encode) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] data = new byte[1024]; int len = 0; String result=""; if (inputStream != null) { try { while ((len = inputStream.read(data)) != -1) { outputStream.write(data,0,len); } result=new String(outputStream.toByteArray(),encode); } catch (IOException e) { e.printStackTrace(); } } return result; } @Override public void run() { //經過Map設置請求字符串。
try {
url = new URL(PATH);
} catch (Exception e) {
e.printStackTrace();
}
Map<String, String> params = new HashMap<String, String>(); params.put("username", "admin"); params.put("password", "123"); String result=sendPostMessage(params, "utf-8"); System.out.println(result); } }
那麼哪一個客戶端更好些呢?在2.2以前使用httpClient能夠避免一些bug。而在2.3以後的版本中使用HttpURLConnection是最好的選擇,簡單的API適合於Android開發。透明壓縮和應答數據的捕獲,減小了網絡的使用,提高了性能而且減小了電池的消耗。性能
在隨後咱們介紹的開源庫中,volley在2.3以前的版本中使用了HttpClient,而在2.3以後使用了HttpURLConnection;android-async-http中封裝了HttpClient。this