1. 程式人生 > >第八章 網絡的時代—網絡開發(2)

第八章 網絡的時代—網絡開發(2)

dex pre header for pen eba 協議名稱 host 會有

8.3基於最成熟的Web協議—HTTP協議編程

8.3.1 HTTP協議簡單介紹

超文本傳輸協定(HTTPHyperTextTransferProtocol)是互聯網上應用最為廣泛的一種網絡協議。

全部的WWW文件都必須遵守這個標準。設計HTTP最初的目的是為了提供一種公布和接收HTML頁面的方法。

HTTP是一個client和server端請求和應答的標準(TCP)。client是終端用戶,server端是站點。通過使用Web瀏覽器、網絡爬蟲或者其它的工具,client發起一個到server上指定port(默認port為80)的HTTP請求。

我們稱這個client為用戶代理(useragent)。

應答的server上存儲著一些資源,比方HTML文件和圖像。我們稱這個應答server為源server。在用戶代理和源server中間可能存在多個中間層,比方代理,網關,或者隧道(tunnel)。雖然TCP/IP協議是互聯網上最流行的應用,HTTP協議並沒有規定必須使用它和基於它支持的層。

其實,HTTP能夠在不論什麽其它互聯網協議上。或者在其它網絡上實現。HTTP僅僅假定其下層協議提供可靠的傳輸,不論什麽能夠提供這樣的保證的協議都能夠被其使用。

通常,由HTTPclient發起一個請求,建立一個到server指定port(默認是80port)的TCP連接。HTTPserver則在那個port監聽client發送過來的請求。

一旦收到請求,server向client發回一個狀態行,比方“HTTP/1.1200 OK”,和響應的消息,消息的消息體可能是請求的文件、錯誤消息、或者其它一些信息。

HTTP使用TCP而不是UDP的原因在於打開一個網頁必須傳送很多數據。而TCP協議提供傳輸控制。按順序組織數據,和錯誤糾正。

通過HTTP或者HTTPS協議請求的資源,由統一資源標識符(UniformResource IdentifiersURI)來標識。

HTTP/1.1協議中共定義了八種方法(有時也叫“動作”)來表明Request-URI指定的資源的不同操作方式:

1OPTIONS

返回server針對特定資源所支持的HTTP請求方法。也能夠利用向Web

server發送‘*‘的請求來測試server的功能性。

2HEAD

向server索要與GET請求相一致的響應,僅僅只是響應體將不會被返回。這一方法能夠在不必傳輸整個響應內容的情況下,就能夠獲取包括在響應消息頭中的元信息。

3GET

向特定的資源發出請求。

註意:GET方法不應當被用於產生“副作用”的操作中,比如在WebApplication中。當中一個原因是GET可能會被網絡蜘蛛等任意訪問。

參見安全方法

4POST

向指定資源提交數據進行處理請求(比如提交表單或者上傳文件)。數據被包括在請求體中。POST請求可能會導致新的資源的建立和/或已有資源的改動。

5PUT

向指定資源位置上傳其最新內容。

6DELETE

請求server刪除Request-URI所標識的資源。

7TRACE

回顯server收到的請求,主要用於測試或診斷。

8CONNECT

HTTP/1.1協議中預留給能夠將連接改為管道方式的代理server。

當某個請求所針對的資源不支持相應的請求方法的時候,server應當返回狀態碼405MethodNot Allowed);當server不認識或者不支持相應的請求方法的時候,應當返回狀態碼501NotImplemented)。

HTTPserver至少應該實現GETHEAD方法。其它方法都是可選的。

當然。全部的方法支持的實現都應當符合下述的方法各自的語義定義。此外。除了上述方法。特定的HTTPserver還能夠擴展自己定義的方法。

AndroidSDK提供了多個封裝的類,能夠很方便的實現基於HTML協議的編程。一般在,在Android中針對HTTP進行網絡通信有幾種方式:一種是通過URL類獲取網絡資源。一種是使用HttpURLConnection類(一般通過用URL類的openConnection()方法創建一個HttpURLConnection對象)來實現。一種是使用ApacheHTTPclient組件HttpClient實現。以下會對這幾種方式做詳細的說明。

8.3.2 使用URL類讀取HTTP資源

URLUniformResourceLocator)對象代表統一資源定位器。它是指向互聯網“資源”的指針。通常情況下,URL由協議名,主機,port,資源組成。例如以下:

http://www.your-host:80/index.php

URL類經常使用的方法有:

StringgetFile();//獲取此URL的資源名

StringgetHost();//獲取此URL的主機名

StringgetPath();//獲取此URL的路勁

IntgetPort();//獲取此URL的port號

StringgetProtocol();//獲取此URL的協議名稱

StringgetQuery();//獲取此URL的查詢字符串

URLConnectionopenConnection();//返回一個URLConnection對象

InputStreamopenStream();//打開連接,並返回一個用於讀取該URL資源的InputStream


URL對象提供了openStream()方法,就能夠讀取該URL資源的InputStream,很的方便。以下的代碼演示樣例。訪問了Web地址“http://www.google.cn/”。並且將server返回的HTML文本輸出出來。

//import

publicclass URLTest extends Activity {

@Override

publicvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

TextViewtv = new TextView(this);

StringmyString="";

try{

//定義獲取文件內容的URL

URLmURL = new URL("http://www.google.cn/");

//打開URL鏈接

//讀取數據

InputStreamis = mURL.openStream();

BufferedInputStreambis = new BufferedInputStream(is);

//使用用ByteArrayBuffer緩存

ByteArrayBufferbaf = new ByteArrayBuffer(50);

intcurrent = 0;

while((current= bis.read()) != -1) {

baf.append((byte)current);

}

//將緩存的內容轉化為String,UTF-8編碼

myString= EncodingUtils.getString(baf.toByteArray(), "UTF-8");

}catch(Exception e) {

myString= e.getMessage();

}

//設置屏幕顯示

tv.setText(myString);

this.setContentView(tv);

}

}


須要特別註意的是,這裏僅僅是簡單舉個樣例簡單說明怎樣讀取URL網絡資源。能夠看到。我們將全部的代碼都寫到了ActivityonCreate()中。在真實的項目開發過程中,這樣的方式是有問題的。因為網絡的堵塞,可能會出現ANRApplicationNot Response)錯誤,導致程序退出。正常的做法,應該是使用異步的方式請求網絡數據。後面會有詳細的樣例說明詳細怎樣做。


經驗分享:

為了避免頻繁讀取字節流。提高讀取效率,用BufferedInputStream緩存讀到的字節流。

InputStreamis = mURL.openStream();

BufferedInputStreambis =new BufferedInputStream(is);

//準備好BufferdInputStream後,我們就能夠用read方法讀入網絡數據

ByteArrayBufferbaf = new ByteArrayBuffer(50);

intcurrent = 0;

while((current= bis.read())! = -1) {

baf.append((byte)current);

}

因為讀到的數據僅僅是字節流,無法直接顯示到屏幕上,所以我們得在顯示之前將字節流轉換為可讀取的字符串。

假設讀取的是.txt等文件是UTF-8格式的。就須要對數據進行專門的轉換

myString= EncodingUtils.getString(baf.toByteArray(),"UTF-8");


8.3.3使用HttpURLConnection類訪問HTTP資源

HttpURLConnection繼承於URLConnection。它在URLConnection的基礎上提供了例如以下的便捷的方法。

voidsetResponseMethod(String method);//設置發送請求

intgetResponseCode();//獲取server的響應代碼

StringgetResponseMessage();//獲取server的響應消息

StringgetResponseMethod();//獲取發送請求


使用HttpURLConnection類訪問HTTP資源的基本過程例如以下:

1)創建URL以及HttpURLConnection對象。

2)設置連接參數。

3)連接到server。

4)向server寫數據。

5)從server讀取數據。

以下提供一段代碼說明詳細怎樣實現。

try {

//創建一個URL對象

URLurl = new URL(your_url);

//創建一個URL連接。假設有代理的話能夠指定一個代理。

URLConnectionconnection = url.openConnection(Proxy_yours);

//對於HTTP連接能夠直接轉換成HttpURLConnection。這樣就能夠使用一些HTTP連接特定的方法。如setRequestMethod():HttpURLConnectionconnection = (HttpURLConnection)url.openConnection(Proxy_yours);


//在開始和server連接之前,可能須要設置一些網絡參數

connection.setConnectTimeout(10000);

//連接到server

connection.connect();

//與server交互:

OutputStreamoutStream = connection.getOutputStream();

ObjectOutputStreamobjOutput = new ObjectOutputStream(outStream);

objOutput.writeObject(newString(“this is a string…”));

objOutput.flush();

InputStreamin = connection.getInputStream();

//處理數據 省略詳細代碼…

}catch (Exception e) {

//網絡讀寫操作往往會產生一些異常,所以在詳細編寫網絡應用時

//最好捕捉每個詳細以採取相應措施

}


8.3.4使用ApacheHttpClient

ApacheHttpClient 是一個開源項目。它對java.net中的類進行了封裝,彌補了java.net.*靈活性不足的缺點,更適合在Android中開發網絡應用,支持client的HTTP編程,更加方便高效。

一般的。使用HttpClient進行網絡開發的過程例如以下:

1)創建HttpClient對象。

2)假設須要發送GET請求,創建HttpGet對象;假設須要發送Post請求。創建HttpPost對象。

3)假設須要設置請求參數,可使用HttpGetHttpPost共同的setParams(HttpParamsparams)方法來加入請求參數。HttpPost還能夠調用setEntity(HttpEntityentity)方法來設置。

4)調用HttpClient對象的execute(HttpUriRequestrequest)發送請求。運行該方法返回一個HttpResponse

5)調用HttpResponsegetAllHeaders()getHeaders(Stringname)等方法可獲取響應頭。調用HttpResponsegetEntity()方法可獲取HttpEntity對象,該對象封裝了響應的內容。

以下是一個詳細的通用的網絡連接類,包括了GETPOST的完整代碼及註解。

//import

publicclass HttpConnecter {

/**

*封裝的Get方法

*/

publicstatic String get(String uri) throws ClientProtocolException,IOException {

//獲取系統默認的HttpClient鏈接

HttpClienthttpClient = new DefaultHttpClient();

HttpGethttpGet = new HttpGet(uri);

//發送GET請求

HttpResponsehttpResponse = httpClient.execute(httpGet);

intstatusCode = httpResponse.getStatusLine().getStatusCode();

//獲取server響應信息。200代表成功響應

if(statusCode >= 200 && statusCode < 400) {

StringBuilderstringBuilder = new StringBuilder();

//httpResponse.getEntity().getContent()用來獲取響應的內容

BufferedReaderreader = new BufferedReader(new InputStreamReader(

httpResponse.getEntity().getContent(),"UTF-8"));

for(String s = reader.readLine(); s != null; s = reader.readLine()) {

//讀出內容

stringBuilder.append(s);

}

reader.close();

Log.d("HttpConnecter","HTTPGET:" + uri.toString());

Log.d("HttpConnecter","Response:"+ stringBuilder.toString());

returnstringBuilder.toString();

}

returnnull;

}


/**

*封裝的Post方法

*/

publicstatic String post(String uri, List<NameValuePair>formparams)

throwsClientProtocolException, IOException {

HttpClienthttpClient = new DefaultHttpClient();

UrlEncodedFormEntityentity

=new UrlEncodedFormEntity(formparams,"UTF-8");

HttpPosthttpPost = new HttpPost(uri);

//設置請求參數

httpPost.setEntity(entity);

//發送Post請求

HttpResponsehttpResponse = httpClient.execute(httpPost);

intstatusCode = httpResponse.getStatusLine().getStatusCode();

if(statusCode >= 200 && statusCode < 400) {

StringBuilderstringBuilder = new StringBuilder();

//httpResponse.getEntity().getContent()用來獲取響應的內容

BufferedReaderreader = new BufferedReader(new InputStreamReader(

httpResponse.getEntity().getContent(),"UTF-8"));

for(String s = reader.readLine(); s != null; s = reader.readLine()) {

stringBuilder.append(s);

}

reader.close();

Log.d("HttpConnecter","HTTPPOST:" + uri.toString());

Log.d("HttpConnecter","Response:"+ stringBuilder.toString());

returnstringBuilder.toString();

}

returnnull;

}

}


從上面的編程過程我們能夠看出。使用ApacheHttpClient更加簡單,並且它比HttpURLConnection提供了很多其它的功能。所以普通情況下,我們能夠在項目中用HttpClient封裝一些GetPost、下載、上傳的接口,以供其它代碼直接調用。


經驗分享:

在實現Android網絡應用的開發過程中。須要特別留意兩個問題:一個是網絡流量的問題。還有一個是網絡連接可能不穩定的問題。

對於Android設備的上網方式。一般的有WIFI3G2G幾種方式。

對於WIFI的用戶。對於流量不會太在意。而對於3G、甚至2G上網的用戶來說。流量是關系到用戶錢包的大問題。所以,對於整個應用的設計,就要充分考慮流量的問題。

或者在項目後期做單獨的優化工作。比方,假設應用中須要輪詢server獲取信息,那麽我們就能夠依據用戶的上網方式,自己主動調整輪詢時間,為3G2G的用戶節省流量。這裏僅僅是舉這樣一個樣例。詳細的,還要依據業務需求進行細致挖掘。

用戶使用Android設備,一般都是碎片時間。可能是在辦公室,也可能是在乘坐公交車或者地鐵,網絡信號未必會一直穩定。網絡連接可能會時斷時續。

我們在設計網絡應用的時候。就要充分考慮這樣的情況。一個是要考慮怎樣對網絡連接異常進行處理,一個是要考慮網絡恢復後怎樣處理。


第八章 網絡的時代—網絡開發(2)