編輯:Android開發實例
Android系統中本身是有下載機制的,比如浏覽器使用的DownloadManager。可遺憾的是,DownloadManager只提供給浏覽器使用,一般的應用程序沒法調用它。 另外,如果下載調用頻繁的話,使用DownloadManager其實是很沒有效率的做法。為了解決這些問題,我想我們最好的辦法就是自己實現下載,本文就是基於HTTP協議的下載的一些簡單介紹。
一、HTTP協議簡介
HTTP是一個屬於應用層的面向對象的協議,由於其簡捷、快速的方式,適用於分布式超媒體信息系統。它於1990年提出,經過幾年的使用與發展,得到不斷地完善和擴展。目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的規范化工作正在進行之中,而且HTTP-NG(Next Generation of HTTP)的建議已經提出。
HTTP協議的主要特點可概括如下:
1.支持客戶/服務器模式。
2.簡單快速:客戶向服務器請求服務時,只需傳送請求方法和路徑。請求方法常用的有GET、HEAD、POST。每種方法規定了客戶與服務器聯系的類型不同。由於HTTP協議簡單,使得HTTP服務器的程序規模小,因而通信速度很快。
3.靈活:HTTP允許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type加以標記。
4.無連接:無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開連接。采用這種方式可以節省傳輸時間。
5.無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺少狀態意味著如果後續處理需要前面的信息,則它必須重傳,這樣可能導致每次連接傳送的數據量增大。另一方面,在服務器不需要先前信息時它的應答就較快。
1.1 URL
HTTP URL (URL是一種特殊類型的URI,包含了用於查找某個資源的足夠的信息)的格式如下:
http://host[":"port][abs_path]
http表示要通過HTTP協議來定位網絡資源;
host表示合法的Internet主機域名或者IP地址;
port指定一個端口號,為空則使用缺省端口80;
abs_path指定請求資源的URI;
注:如果URL中沒有給出abs_path,那麼當它作為請求URI時,必須以“/”的形式給出,通常這個工作浏覽器自動幫我們完成。
例如:
1、輸入:www.guet.edu.cn
浏覽器自動轉換成:http://www.guet.edu.cn/
2、http:192.168.0.116:8080/index.jsp
1.2 請求
http請求由三部分組成,分別是:請求行、消息報頭、請求正文。
1.2.1 請求行
請求行以一個方法符號開頭,以空格分開,後面跟著請求的URI和協議的版本,格式如下:
Method Request-URI HTTP-Version CRLF
其中:
Method表示請求方法;
Request-URI是一個統一資源標識符;
HTTP-Version表示請求的HTTP協議版本;
CRLF表示回車和換行(除了作為結尾的CRLF外,不允許出現單獨的CR或LF字符)。
例如:
POST /hello.htm HTTP/1.1(“/r/n”)
1) 請求方法:
請求方法(所有方法全為大寫)有多種,各個方法的解釋如下:
GET 請求獲取Request-URI所標識的資源
POST 在Request-URI所標識的資源後附加新的數據
HEAD 請求獲取由Request-URI所標識的資源的響應消息報頭
PUT 請求服務器存儲一個資源,並用Request-URI作為其標識
DELETE 請求服務器刪除Request-URI所標識的資源
TRACE 請求服務器回送收到的請求信息,主要用於測試或診斷
CONNECT 保留將來使用
OPTIONS 請求查詢服務器的性能,或者查詢與資源相關的選項和需求
2) Request-URI:
用於標識要訪問的網絡資源。通常只要給出相對於服務器的根目錄的相對目錄即可,因此以“/”開頭。
3) 協議版本。
1.2.2 消息報頭
HTTP消息由客戶端到服務器的請求和服務器到客戶端的響應組成。請求消息和響應消息都是由開始行(對於請求消息,開始行就是請求行,對於響應消息,開始行就是狀態行),消息報頭(可選),空行(只有CRLF的行),消息正文(可選)組成。
1) 普通報頭:
在普通報頭中,有少數報頭域用於所有的請求和響應消息,但並不用於被傳輸的實體,只用於傳輸的消息。
Cache-Control:用於指定緩存指令,緩存指令是單向的(響應中出現的緩存指令在請求中未必會出現),且是獨立的(一個消息的緩存指令不會影響另一個消息處理的緩存機制),HTTP1.0使用的類似的報頭域為Pragma。
請求時的緩存指令包括:no-cache(用於指示請求或響應消息不能緩存)、no-store、max-age、max-stale、min-fresh、only-if-cached;
響應時的緩存指令包括:public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age、s-maxage 。
Date:普通報頭域表示消息產生的日期和時間。
Connection:普通報頭域允許發送指定連接的選項。例如指定連接是連續,或者指定“close”選項,通知服務器,在響應完成後,關閉連接。
2) 請求報頭:
允許客戶端向服務器端傳遞請求的附加信息以及客戶端自身的信息。常用的請求報頭如下:
Accept:
Accept請求報頭域用於指定客戶端接受哪些類型的信息。eg:Accept:image/gif,表明客戶端希望接受GIF圖象格式的資源;Accept:text/html,表明客戶端希望接受html文本。
Accept-Charset:
Accept-Charset請求報頭域用於指定客戶端接受的字符集。eg:Accept-Charset:iso-8859-1,gb2312.如果在請求消息中沒有設置這個域,缺省是任何字符集都可以接受。
Accept-Encoding:
Accept-Encoding請求報頭域類似於Accept,但是它是用於指定可接受的內容編碼。eg:Accept-Encoding:gzip.deflate.如果請求消息中沒有設置這個域服務器假定客戶端對各種內容編碼都可以接受。
Accept-Language:
Accept-Language請求報頭域類似於Accept,但是它是用於指定一種自然語言。eg:Accept-Language:zh-cn.如果請求消息中沒有設置這個報頭域,服務器假定客戶端對各種語言都可以接受。
Authorization:
Authorization請求報頭域主要用於證明客戶端有權查看某個資源。當浏覽器訪問一個頁面時,如果收到服務器的響應代碼為401(未授權),可以發送一個包含Authorization請求報頭域的請求,要求服務器對其進行驗證。
Host(發送請求時,該報頭域是必需的):
Host請求報頭域主要用於指定被請求資源的Internet主機和端口號,它通常從HTTP URL中提取出來的。
eg:我們在浏覽器中輸入:http://www.guet.edu.cn/index.html。浏覽器發送的請求消息中,就會包含Host請求報頭域,如下:
Host:www.guet.edu.cn
此處使用缺省端口號80,若指定了端口號,則變成:Host:www.guet.edu.cn:指定端口號
User-Agent:
我們上網登陸論壇的時候,往往會看到一些歡迎信息,其中列出了你的操作系統的名稱和版本,你所使用的浏覽器的名稱和版本,這往往讓很多人感到很神奇,實際上,服務器應用程序就是從User-Agent這個請求報頭域中獲取到這些信息。User-Agent請求報頭域允許客戶端將它的操作系統、浏覽器和其它屬性告訴服務器。不過,這個報頭域不是必需的,如果我們自己編寫一個浏覽器,不使用User-Agent請求報頭域,那麼服務器端就無法得知我們的信息了。
請求報頭舉例:
GET /form.html HTTP/1.1 (CRLF)
Accept:image/gif,image/x-xbitmap,image/jpeg,application/x-shockwave-flash,application/vnd.ms-excel,application/vnd.ms-
powerpoint,application/msword,*/* (CRLF)
Accept-Language:zh-cn (CRLF)
Accept-Encoding:gzip,deflate (CRLF)
If-Modified-Since:Wed,05 Jan 2007 11:21:25 GMT (CRLF)
If-None-Match:W/"80b1a4c018f3c41:8317" (CRLF)
User-Agent:Mozilla/4.0(compatible;MSIE6.0;Windows NT 5.0) (CRLF)
Host:www.guet.edu.cn (CRLF)
Connection:Keep-Alive (CRLF)
(CRLF)
3) 響應報頭:
響應報頭允許服務器傳遞不能放在狀態行中的附加響應信息,以及關於服務器的信息和對Request-URI所標識的資源進行下一步訪問的信息。
常用的響應報頭:
Location:
Location響應報頭域用於重定向接受者到一個新的位置。Location響應報頭域常用在更換域名的時候。
Server:
Server響應報頭域包含了服務器用來處理請求的軟件信息。與User-Agent請求報頭域是相對應的。下面是Server響應報頭域的一個例子:
Server:Apache-Coyote/1.1
WWW-Authenticate:
WWW-Authenticate響應報頭域必須被包含在401(未授權的)響應消息中,客戶端收到401響應消息時候,並發送Authorization報頭域請求服務器對其進行驗證時,服務端響應報頭就包含該報頭域。eg:WWW-Authenticate:Basic realm="Basic Auth Test!" //可以看出服務器對請求資源采用的是基本驗證機制。
4) 實體報頭:
請求和響應消息都可以傳送一個實體。一個實體由實體報頭域和實體正文組成,但並不是說實體報頭域和實體正文要在一起發送,可以只發送實體報頭域。實體報頭定義了關於實體正文(eg:有無實體正文)和請求所標識的資源的元信息。
常用的實體報頭:
Content-Encoding:
Content-Encoding實體報頭域被用作媒體類型的修飾符,它的值指示了已經被應用到實體正文的附加內容的編碼,因而要獲得Content-Type報頭域中所引用的媒體類型,必須采用相應的解碼機制。Content-Encoding這樣用於記錄文檔的壓縮方法。eg:Content-Encoding:gzip
Content-Language:
Content-Language實體報頭域描述了資源所用的自然語言。沒有設置該域則認為實體內容將提供給所有的語言閱讀者。eg:Content-Language:da
Content-Length:
Content-Length實體報頭域用於指明實體正文的長度,以字節方式存儲的十進制數字來表示。
Content-Type:
Content-Type實體報頭域用語指明發送給接收者的實體正文的媒體類型。eg:Content-Type:text/html;charset=ISO-8859-1、Content-Type:text/html;charset=GB2312
Last-Modified:
Last-Modified實體報頭域用於指示資源的最後修改日期和時間。
Expires:
Expires實體報頭域給出響應過期的日期和時間。為了讓代理服務器或浏覽器在一段時間以後更新緩存中(再次訪問曾訪問過的頁面時,直接從緩存中加載,縮短響應時間和降低服務器負載)的頁面,我們可以使用Expires實體報頭域指定頁面過期的時間。eg:Expires:Thu,15 Sep 2006 16:23:12 GMT
HTTP1.1的客戶端和緩存必須將其他非法的日期格式(包括0)看作已經過期。eg:為了讓浏覽器不要緩存頁面,我們也可以利用Expires實體報頭域,設置為0,jsp中程序如下:response.setDateHeader("Expires","0");
1.3 響應
在接收和解釋請求消息後,服務器返回一個HTTP響應消息。HTTP響應也是由三個部分組成,分別是:狀態行、消息報頭、響應正文。
主要說一下狀態行。狀態行格式如下:
HTTP-Version Status-Code Reason-Phrase CRLF
其中:
HTTP-Version表示服務器HTTP協議的版本;
Status-Code表示服務器發回的響應狀態代碼;
Reason-Phrase表示狀態代碼的文本描述。
狀態代碼有三位數字組成,第一個數字定義了響應的類別,且有五種可能取值:
1xx:指示信息--表示請求已接收,繼續處理
2xx:成功--表示請求已被成功接收、理解、接受
3xx:重定向--要完成請求必須進行更進一步的操作
4xx:客戶端錯誤--請求有語法錯誤或請求無法實現
5xx:服務器端錯誤--服務器未能實現合法的請求
常見狀態代碼、狀態描述、說明:
200 OK //客戶端請求成功
400 Bad Request //客戶端請求有語法錯誤,不能被服務器所理解
401 Unauthorized //請求未經授權,這個狀態代碼必須和WWW-Authenticate報頭域一起使用
403 Forbidden //服務器收到請求,但是拒絕提供服務
404 Not Found //請求資源不存在,eg:輸入了錯誤的URL
500 Internal Server Error //服務器發生不可預期的錯誤
503 Server Unavailable //服務器當前不能處理客戶端的請求,一段時間後,可能恢復正常
eg:HTTP/1.1 200 OK (CRLF)
二、HTTP協議之下載
在了解了HTTP協議的基本規則之後,我們就可以將其應用於文件的下載了。這一節將介紹的是,通過HTTP協議下載的原理。
2.1 文件請求
向服務器發送如下請求:
GET /Path/FileName HTTP/1.0
Host: www.server.com:80
Accept: */*
User-Agent: GeneralDownloadApplication
Connection: close
每行用一個“回車換行”分隔,末尾再追加一個“回車換行”作為整個請求的結束。
Host字段表示主機名和端口號,如果端口號是默認的80則可以不寫。
Accept字段中的*/*表示接收任何類型的數據。
User-Agent表示用戶代理,這個字段可有可無,但強烈建議加上,因為它是服務器統計、追蹤以及識別客戶端的依據。
Connection字段中的close表示使用非持久連接。
2.2 服務器應答
如果服務器成功收到該請求,並且沒有出現任何錯誤,則會返回類似下面的數據:
HTTP/1.0 200 OK
Content-Length: 13057672
Content-Type: application/octet-stream
Last-Modified: Wed, 10 Oct 2005 00:56:34 GMT
Accept-Ranges: bytes
ETag: "2f38a6cac7cec51:160c"
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Date: Wed, 16 Nov 2005 01:57:54 GMT
Connection: close
Content-Length字段是一個比較重要的字段,它標明了服務器返回數據的長度,這個長度是不包含HTTP頭長度的。換句話說,我們的請求中並沒有Range字段(後面會說到),表示我們請求的是整個文件,所以Content-Length就是整個文件的大小。其余各字段是一些關於文件和服務器的屬性信息。
這段返回數據同樣是以最後一行的結束標志(回車換行)和一個額外的回車換行作為結束,即“\r\n\r\n”。而“\r\n\r\n”後面緊接的就是文件的內容了,這樣我們就可以找到“\r\n\r\n”,並從它後面的第一個字節開始,源源不斷的讀取,再寫到文件中了。
2.3 斷點續傳
斷點續傳的實現非常簡單,只要在請求中加一個Range字段就可以了。假如一個文件有1000個字節,那麼其范圍就是0-999,則:
Range: bytes=500- 表示讀取該文件的500-999字節,共500字節。
Range: bytes=500-599 表示讀取該文件的500-599字節,共100字節。
Range還有其它幾種寫法,但上面這兩種是最常用的,對於斷點續傳也足矣了。如果HTTP請求中包含Range字段,那麼服務器會返回206(Partial Content),同時HTTP頭中也會有一個相應的Content-Range字段,類似下面的格式:
Content-Range: bytes 500-999/1000
Content-Range字段說明服務器返回了文件的某個范圍及文件的總長度。這時Content-Length字段就不是整個文件的大小了,而是對應文件這個范圍的字節數,這一點一定要注意。
2.4 重定向
很多軟件下載網站的文件下載鏈接都是通過程序重定向的,比如pchome的ACDSee的HTTP下載地址是:
http://download.pchome.net/php/tdownload2.php?sid=5547&url=/multimedia/viewer/acdc31sr1b051007.exe&svr=1&typ=0
這種地址並沒有直接標識文件的位置,而是通過程序進行了重定向。如果向服務器請求這樣的URL,服務器就會返回302(Moved Temporarily),意思就是需要重定向,同時在HTTP頭中會包含一個Location字段,Location字段的值就是重定向後的目的URL。這時就需要斷開當前的連接,而向這個重定向後的服務器發請求。
三、HttpClient
雖然在 JDK 的 java.net 包中已經提供了訪問 HTTP 協議的基本功能,但是對於大部分應用程序來說,JDK 庫本身提供的功能還不夠豐富和靈活。HttpClient 是 Apache Jakarta Common 下的子項目,用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,並且它支持 HTTP 協議最新的版本和建議。HttpClient 已經應用在很多的項目中,比如 Apache Jakarta 上很著名的另外兩個開源項目 Cactus 和 HTMLUnit 都使用了 HttpClient。HttpClient 項目非常活躍,使用的人還是非常多的。目前 HttpClient 版本是在 2005.10.11 發布的 3.0 RC4 。
HttpClient的主要功能有以下一些:
1) 實現了所有 HTTP 的方法(GET,POST,PUT,HEAD 等);
2) 支持自動轉向;
3) 支持 HTTPS 協議;
4) 支持代理服務器等。
3.1 環境搭建及所需包
需要Java開發環境JDK,需要可以訪問網絡。Android程序需要有“android.permission.INTERNET”的permission。
所需包:
1、commons-httpclient-3.1.jar: 包括Http協議所需的類。
2、commons-logging-1.1.jar: 包括記錄程序運行時的活動日志記錄的類。
3、commons-codec-1.3.jar: 包括編碼解碼的類。
這些包都是Apache的開源項目,可以在Apache開源組織官網http://www.apache.org/上找到。
3.2 HttpClient實現HTTP協議基本通信操作
在實現所有操作之前必須首先實例化一個HttpClient,即初始化一個客戶端。
3.2.1 請求
以GET請求為例。
a、實例化一個請求方法。
注:
① 雖然Google已經將服務器搬出了大陸,但是HttpClient可實現自動轉向,即自動重定向。所以當服務器返回的狀態代碼為3××時,將自動重定向,知道到達文件實際位置)。
② GetMethod構造函數中的字符串表示的是文件的URI地址。這裡只是因為之前沒有指定服務器主機地址,所以需要完整名。其實也可以這樣:
b、添加需要的消息報頭信息。
HttpClient會構建必須的消息報頭信息,如果沒有特殊要求可以不用修改。但如果需要在消息報頭添加一些特殊信息,例如下載時需要斷點續傳等,則可用上述方法修改。
c、發出請求(執行命令)。
此時,程序實際向服務器發出請求,連接成功後,函數返回,返回值為狀態代碼。
3.2.2 響應
接上例。
a、返回狀態代碼。
上例中的“statusCode”即為狀態代碼。除此方法之外,還可以:
注:在httpclient的包中有一個名為“HttpStatus”類,其中定義了大多數的狀態代碼。如:
HttpStatus.SC_OK
HttpStatus.SC_FORBIDDEN 等。
b、響應報頭。
獲取所有服務器端返回的響應報頭。
獲取響應報頭中指定的鍵值對。
之後可以通過調用header.getName()、header.getValue()來得到相關信息。
c、響應正文。
InputStream inputStream = method.getResponseBodyAsStream();
3.2.3 斷開連接
以上三種方法,視情況選用。
斷開連接。
3.2.4 其他
其他包括一些和下載無關,但卻非常基本和有用的東西
a、POST數據。
POST請求和GET請求大致相同,唯一需要注意的是,如何在POST信息中加入自己所需傳輸的信息。
轉自:http://www.blogjava.net/zh-weir/archive/2010/05/02/319892.html
postMethod.setRequestBody(InputStream body);postMethod.setRequestBody(NameValuePair[] parameterBody);
postMethod.setRequestBody(String body);
b、代理服務器。
只需指定httpClient的實例的代理就可以了,基於此實例的所有操作將經由此代理。
c、字符編碼。
某目標頁的編碼可能出現在兩個地方:
第一個地方是服務器返回的http頭中(RequestHeader的Content-Type、Content-Encoding字段);
另外一個地方是得到的html/xml頁面中。如:
<meta http-equiv="Content-Type" content="text/html; charset=gb2312"/>
或者 <?xml version="1.0" encoding="gb2312"?>
d、自動跳轉。
HttpClient對GET請求可實現自動跳轉。但是對於POST和PUT請求要求接受後繼服務的,暫不支持自動跳轉。
當服務器返回的狀態代碼為3××時,需要根據消息報頭的“Location”字段的地址來實現跳轉。注意,“Location”字段的地址可能是相對地址,需要自己進行處理。
還有一種可能就是在頁面中實現的跳轉。例如,在HTML中,<meta http-equiv="refresh" content="5; url=http://www.ibm.com/us">。
e、Https協議。
參看: 《HttpClient入門》 。
可以顯示在的Android任務,通過加載進度條的進展。進度條有兩種形狀。加載欄和加載微調(spinner)。在本章中,我們將討論微調(spinner)。Spinner 用
登錄應用程序的屏幕,詢問憑據登錄到一些特定的應用。可能需要登錄到Facebook,微博等本章介紹了,如何創建一個登錄界面,以及如何管理安全問題和錯誤嘗試。首先,必須定義兩
登錄應用程序的屏幕,詢問憑據登錄到一些特定的應用。可能需要登錄到Facebook,微博等本章介紹了,如何創建一個登錄界面,以及如何管理安全問題和錯誤嘗試。首先,必須定義兩
Android應用程序可以在許多不同地區的許多設備上運行。為了使應用程序更具交互性,應用程序應該處理以適合應用程序將要使用的語言環境方面的文字,數字,文件等。在本章中,我