在Android中,提供了標準Java介面HttpURLConnection和Apache介面HttpClient,為用戶端HTTP編程提供了豐富的支援。
在HTTP通訊中使用最多的就是GET和POST了,GET請求可以擷取靜態頁面,也可以把參數放在URL字串的後面,傳遞給伺服器。POST與GET的不同之處在於POST的參數不是放在URL字串裡面,而是放在HTTP請求資料中。
本文將使用標準Java介面HttpURLConnection,以一個執行個體示範如何使用POST方式向伺服器提交資料,並將伺服器的響應結果顯示在Android用戶端。
1.伺服器端的準備
為了完成該執行個體,我們需要在伺服器端做以下準備工作:
(1)我們需要在MyEclipse中建立一個Web工程,用來類比伺服器端的Web服務,這裡,我將該工程命名為了“myhttp”。
(2)修改該工程的“index.jsp”檔案,添加兩個輸入框和一個提交按鈕,作為該Web工程的顯示頁面。運行Tomcat,在瀏覽器中訪問該Web工程,可以看到1所示的介面。
圖1 Web工程的顯示頁面
(3)在該Web工程中,建立一個繼承自HttpServlet的LoginAction類,並實現其中的doPost()方法,用來響應圖1所示頁面的使用者操作。具體實現如下:
1 public void doPost(HttpServletRequest request, HttpServletResponse response) 2 throws ServletException, IOException { 3 4 response.setContentType("text/html;charset=utf-8"); 5 request.setCharacterEncoding("utf-8"); 6 response.setCharacterEncoding("utf-8"); 7 PrintWriter out = response.getWriter(); 8 9 String username = request.getParameter("username");10 String password = request.getParameter("password");11 12 //判斷使用者名稱密碼是否正確13 if(username.equals("admin") && password.equals("123")) {14 out.print("Login succeeded!");15 }else {16 out.print("Login failed!");17 }18 19 out.flush();20 out.close();21 }
由上述代碼可以看出,當我們在圖1所示的頁面輸入使用者名稱“admin”,密碼“123”時,點擊提交按鈕,會得到“Login succeeded!”的提示資訊,2所示。若使用者名稱、密碼錯誤,則會得到“Login failed!”的提示資訊。
圖2 登入成功介面
至此,伺服器端的準備工作就全部完成了。
2.用戶端實現
在Android用戶端,我們需要完成的工作是:以POST方式發送使用者名稱密碼到上述伺服器,並獲得伺服器的驗證資訊。
我們分以下幾個步驟來完成。
2.1 UI介面
在Android工程中,我們需要完成一個簡單的UI介面,用來完成使用者名稱密碼的輸入、發送POST請求、顯示伺服器的驗證結果,完成後的介面3所示。
圖3 用戶端UI介面
在MainActivity中,我們需要擷取兩個EditText控制項的輸入,“提交”按鍵的監聽,以及伺服器驗證結果的TextView內容顯示。具體實現代碼如下:
1 /* 2 * Function : 點擊事件響應 3 * Author : 部落格園-依舊淡然 4 */ 5 public void onClick(View view) { 6 switch(view.getId()) { 7 case R.id.button_submit: 8 String username = mEditText_userName.getText().toString(); 9 String password = mEditText_password.getText().toString();10 Map<String, String> params = new HashMap<String, String>();11 params.put("username", username);12 params.put("password", password);13 mTextView_result.setText(HttpUtils.submitPostData(params, "utf-8"));14 break;15 }16 }
2.2發送POST請求到伺服器
可以看到上述代碼中,我們調用了HttpUtils類的靜態方法submitPostData()完成了發送POST請求到伺服器,並將該方法的傳回值(伺服器的響應結果)顯示在了TextView控制項中。
在HttpUtils類中,submitPostData()方法的具體實現如下:
/* * Function : 發送Post請求到伺服器 * Param : params請求體內容,encode編碼格式 * Author : 部落格園-依舊淡然 */ public static String submitPostData(Map<String, String> params, String encode) { byte[] data = getRequestData(params, encode).toString().getBytes(); //獲得請求體 try { HttpURLConnection httpURLConnection = (HttpURLConnection)url.openConnection(); httpURLConnection.setConnectTimeout(3000); //設定連線逾時時間 httpURLConnection.setDoInput(true); //開啟輸入資料流,以便從伺服器擷取資料 httpURLConnection.setDoOutput(true); //開啟輸出資料流,以便向伺服器提交資料 httpURLConnection.setRequestMethod("POST"); //設定以Post方式提交資料 httpURLConnection.setUseCaches(false); //使用Post方式不能使用緩衝 //佈建要求體的類型是文本類型 httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); //佈建要求體的長度 httpURLConnection.setRequestProperty("Content-Length", String.valueOf(data.length)); //獲得輸出資料流,向伺服器寫入資料 OutputStream outputStream = httpURLConnection.getOutputStream(); outputStream.write(data); int response = httpURLConnection.getResponseCode(); //獲得伺服器的響應碼 if(response == HttpURLConnection.HTTP_OK) { InputStream inptStream = httpURLConnection.getInputStream(); return dealResponseResult(inptStream); //處理伺服器的響應結果 } } catch (IOException e) { e.printStackTrace(); } return ""; }
通過以上的代碼可以看出,在該方法中,其實完成了3件事:
(1)將使用者名稱密碼封裝成請求體,這是通過調用getRequestData()方法來實現的(後面會講到這個方法的具體實現)。
(2)設定HttpURLConnection對象的各種參數(其實是設定HTTP協議請求體的各項參數),然後通過httpURLConnection.getOutputStream()方法獲得伺服器輸出資料流outputStream,再使用outputStream.write()方法將請求體內容發送給伺服器。
(3)判斷伺服器的響應碼,通過httpURLConnection.getInputStream()方法獲得伺服器的響應輸入資料流,然後再調用dealResponseResult()方法處理伺服器的響應結果。
2.3封裝請求體
使用POST請求時,POST的參數不是放在URL字串裡,而是放在HTTP請求資料中,所以我們需要對POST的參數進行封裝。
針對該執行個體而言,我們發送的URL請求是:http://192.168.1.101:8080/myhttp/servlet/LoginAction,但是我們需要將POST的參數(也就是username和password)封裝到該請求中,形成如下的形式:http://192.168.1.101:8080/myhttp/servlet/LoginAction?username=admin&password=123。我們該怎麼做呢?如下的代碼給出了一種實現的方案:
1 /* 2 * Function : 封裝請求體資訊 3 * Param : params請求體內容,encode編碼格式 4 * Author : 部落格園-依舊淡然 5 */ 6 public static StringBuffer getRequestData(Map<String, String> params, String encode) { 7 StringBuffer stringBuffer = new StringBuffer(); //儲存封裝好的請求體資訊 8 try { 9 for(Map.Entry<String, String> entry : params.entrySet()) {10 stringBuffer.append(entry.getKey())11 .append("=")12 .append(URLEncoder.encode(entry.getValue(), encode))13 .append("&");14 }15 stringBuffer.deleteCharAt(stringBuffer.length() - 1); //刪除最後的一個"&"16 } catch (Exception e) {17 e.printStackTrace();18 }19 return stringBuffer;20 }
2.4處理響應結果
最後,我們再來看一看對伺服器返回結果的處理是怎樣的。因為在本執行個體中,伺服器的返回結果是字串“Login succeeded!”或“Login failed!”,所以這裡我們需要做的就是將伺服器的返回結果輸入資料流轉化成字串。當然了,如果伺服器返回的是圖片,那麼,我們就需要就得到的輸入資料流轉化成Bitmap圖片了。如下代碼是上面代碼中用到的dealResponseResult()方法的具體實現。
1 /* 2 * Function : 處理伺服器的響應結果(將輸入資料流轉化成字串) 3 * Param : inputStream伺服器的響應輸入資料流 4 * Author : 部落格園-依舊淡然 5 */ 6 public static String dealResponseResult(InputStream inputStream) { 7 String resultData = null; //儲存處理結果 8 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 9 byte[] data = new byte[1024];10 int len = 0;11 try {12 while((len = inputStream.read(data)) != -1) {13 byteArrayOutputStream.write(data, 0, len);14 }15 } catch (IOException e) {16 e.printStackTrace();17 }18 resultData = new String(byteArrayOutputStream.toByteArray()); 19 return resultData;20 }
2.5運行效果
最後,看看該執行個體的運行效果吧,4所示。
圖4 執行個體運行效果