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

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

8.3.1 HTTP協議簡單介紹

超文本傳輸協定(HTTPHyperTextTransferProtocol)是互聯網上應用最爲普遍的一種網絡協議。php

所有的WWW文件都必須遵照這個標準。設計HTTP最初的目的是爲了提供一種公佈和接收HTML頁面的方法。java

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」,和響應的消息,消息的消息體多是請求的文件、錯誤消息、或者其它一些信息。app

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

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

HTTP/1.1協議中共定義了八種方法(有時也叫「動做」)來代表Request-URI指定的資源的不一樣操做方式:

1OPTIONS

返回server針對特定資源所支持的HTTP請求方法。也可以利用向Webserver發送'*'的請求來測試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設備,通常都是碎片時間。多是在辦公室,也多是在乘坐公交車或者地鐵,網絡信號未必會一直穩定。網絡鏈接可能會時斷時續。

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

相關文章
相關標籤/搜索