1. 程式人生 > >Android開發實現HttpClient工具類以及HttpClient的3種超時說明

Android開發實現HttpClient工具類以及HttpClient的3種超時說明

引子

在Android開發中我們經常會用到網路連線功能與伺服器進行資料的互動,為此Android的SDK提供了Apache的HttpClient來方便我們使用各種Http服務。你可以把HttpClient想象成一個瀏覽器,通過它的API我們可以很方便的發出GET,POST請求(當然它的功能遠不止這些)。

比如你只需以下幾行程式碼就能發出一個簡單的GET請求並列印響應結果:

複製程式碼
try {
// 建立一個預設的HttpClient HttpClient httpclient =new DefaultHttpClient();
// 建立一個GET請求 HttpGet request
=new HttpGet("www.google.com");
// 傳送GET請求,並將響應內容轉換成字串 String response = httpclient.execute(request, new BasicResponseHandler());
Log.v(
"response text", response);
}
catch (ClientProtocolException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
複製程式碼

為什麼要使用單例HttpClient?

這只是一段演示程式碼,實際的專案中的請求與響應處理會複雜一些,並且還要考慮到程式碼的容錯性,但是這並不是本篇的重點。注意程式碼的第三行:

 HttpClient httpclient =new DefaultHttpClient();

在發出HTTP請求前,我們先建立了一個HttpClient物件。那麼,在實際專案中,我們很可能在多處需要進行HTTP通訊,這時候我們不需要為每個請求都建立一個新的HttpClient。因為之前已經提到,HttpClient就像一個小型的瀏覽器,對於整個應用,我們只需要一個HttpClient就夠了。看到這裡,一定有人心裡想,這有什麼難的,用單例啊!!就像這樣:

複製程式碼
publicclass CustomerHttpClient {
privatestatic HttpClient customerHttpClient;

private CustomerHttpClient() {
}

publicstatic HttpClient getHttpClient() {
if(null== customerHttpClient) {
customerHttpClient
=new DefaultHttpClient();
}
return customerHttpClient;
}
}
複製程式碼

那麼,哪裡不對勁呢?或者說做的還不夠完善呢?

多執行緒!試想,現在我們的應用程式使用同一個HttpClient來管理所有的Http請求,一旦出現併發請求,那麼一定會出現多執行緒的問題。這就好像我們的瀏覽器只有一個標籤頁卻有多個使用者,A要上google,B要上baidu,這時瀏覽器就會忙不過來了。幸運的是,HttpClient提供了建立執行緒安全物件的API,幫助我們能很快地得到執行緒安全的“瀏覽器”。

解決多執行緒問題

複製程式碼
publicclass CustomerHttpClient {
privatestaticfinal String CHARSET = HTTP.UTF_8;
privatestatic HttpClient customerHttpClient;

private CustomerHttpClient() {
}

publicstaticsynchronized HttpClient getHttpClient() {
if (null== customerHttpClient) {
HttpParams params
=new BasicHttpParams();
// 設定一些基本引數 HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params,
CHARSET);
HttpProtocolParams.setUseExpectContinue(params,
true);
HttpProtocolParams
.setUserAgent(
params,
"Mozilla/5.0(Linux;U;Android 2.2.1;en-us;Nexus One Build.FRG83) "+"AppleWebKit/553.1(KHTML,like Gecko) Version/4.0 Mobile Safari/533.1");
// 超時設定/* 從連線池中取連線的超時時間 */
ConnManagerParams.setTimeout(params,
1000);
/* 連線超時 */
HttpConnectionParams.setConnectionTimeout(params,
2000);
/* 請求超時 */
HttpConnectionParams.setSoTimeout(params,
4000);

// 設定我們的HttpClient支援HTTP和HTTPS兩種模式 SchemeRegistry schReg =new SchemeRegistry();
schReg.register(
new Scheme("http", PlainSocketFactory
.getSocketFactory(),
80));
schReg.register(
new Scheme("https", SSLSocketFactory
.getSocketFactory(),
443));

// 使用執行緒安全的連線管理來建立HttpClient ClientConnectionManager conMgr =new ThreadSafeClientConnManager(
params, schReg);
customerHttpClient
=new DefaultHttpClient(conMgr, params);
}
return customerHttpClient;
}
}
複製程式碼

在上面的getHttpClient()方法中,我們為HttpClient配置了一些基本引數和超時設定,然後使用ThreadSafeClientConnManager來建立執行緒安全的HttpClient。上面的程式碼提到了3種超時設定,比較容易搞混,故在此特作辨析。

HttpClient的3種超時說明

/* 從連線池中取連線的超時時間 */
ConnManagerParams.setTimeout(params,
1000);
/* 連線超時 */
HttpConnectionParams.setConnectionTimeout(params,
2000);
/* 請求超時 */
HttpConnectionParams.setSoTimeout(params,
4000);

第一行設定ConnectionPoolTimeout:這定義了從ConnectionManager管理的連線池中取出連線的超時時間,此處設定為1秒。

第二行設定ConnectionTimeout:  這定義了通過網路與伺服器建立連線的超時時間。Httpclient包中通過一個非同步執行緒去建立與伺服器的socket連線,這就是該socket連線的超時時間,此處設定為2秒。

第三行設定SocketTimeout:    這定義了Socket讀資料的超時時間,即從伺服器獲取響應資料需要等待的時間,此處設定為4秒。

以上3種超時分別會丟擲ConnectionPoolTimeoutException,ConnectionTimeoutException與SocketTimeoutException。 

封裝簡單的POST請求

有了單例的HttpClient物件,我們就可以把一些常用的發出GET和POST請求的程式碼也封裝起來,寫進我們的工具類中了。目前我僅僅實現發出POST請求並返回響應字串的方法以供大家參考。將以下程式碼加入我們的CustomerHttpClient類中:

複製程式碼
privatestaticfinal String TAG ="CustomerHttpClient";

publicstatic String post(String url, NameValuePair... params) {
try {
// 編碼引數 List<NameValuePair> formparams =new ArrayList<NameValuePair>(); // 請求引數for (NameValuePair p : params) {
formparams.add(p);
}
UrlEncodedFormEntity entity
=new UrlEncodedFormEntity(formparams,
CHARSET);
// 建立POST請求 HttpPost request =new HttpPost(url);
request.setEntity(entity);
// 傳送請求 HttpClient client = getHttpClient();
HttpResponse response
= client.execute(request);
if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
thrownew RuntimeException("請求失敗");
}
HttpEntity resEntity
= response.getEntity();
return (resEntity ==null) ?null : EntityUtils.toString(resEntity, CHARSET);
}
catch (UnsupportedEncodingException e) {
Log.w(TAG, e.getMessage());
returnnull;
}
catch (ClientProtocolException e) {
Log.w(TAG, e.getMessage());
returnnull;
}
catch (IOException e) {
thrownew RuntimeException("連線失敗", e);
}

}
 
複製程式碼

使用我們的CustomerHttpClient工具類

現在,在整個專案中我們都能很方便的使用該工具類來進行網路通訊的業務程式碼編寫了。下面的程式碼演示瞭如何使用username和password註冊一個賬戶並得到新賬戶ID。

複製程式碼
final String url ="http://yourdomain/context/adduser";
//準備資料 NameValuePair param1 =new BasicNameValuePair("username", "張三");
NameValuePair param2
=new BasicNameValuePair("password", "123456");
int resultId =-1;
try {
// 使用工具類直接發出POST請求,伺服器返回json資料,比如"{userid:12}"
String response = CustomerHttpClient.post(url, param1, param2);
JSONObject root
=new JSONObject(response);
resultId
= Integer.parseInt(root.getString("userid"));
Log.i(TAG,
"新使用者ID:"+ resultId);
}
catch (RuntimeException e) {
// 請求失敗或者連線失敗 Log.w(TAG, e.getMessage());
Toast.makeText(
this, e.getMessage(), Toast.LENGTH_SHORT);
}
catch (Exception e) {
// JSon解析出錯 Log.w(TAG, e.getMessage());
}
複製程式碼

結語

可以看到,使用工具類能大大提高在專案中編寫網路通訊程式碼的效率。不過該工具類還有待完善,歡迎各位補充和矯正錯誤,希望最後能完成一個工具類作為使用HttpClient的最佳實踐。(完)