2011-05-31 50 views
2

我的Android應用程序使用HttpURLConnection對象從我們的服務器請求信息。服務器返回一些文本 - 根據請求可能會有一些(〜50個字符)或很多(12 MB)。 MIME類型是文本/純文本。HttpURLConnection getInputStream()有時包含響應標頭

在我最初的實現中,我很失望地發現getInputStream()從服務器返回整個HTTP響應 - 包括頭文件 - 即使文檔說它只包含響應主體。哦,好吧......我實現了一個小狀態機來讀取和存儲頭文件,並在我的代碼中提供接口以允許調用者訪問頭字段。從AsyncTask中調用它來關閉UI線程並獲得簡單的進度跟蹤方法。一切都好。

我最近添加了另一個功能,要求從服務器的信息,所以我用我的同一個「http連接包裝」對象,它希望看到正文流中的標題。這一次,使用Thread對象將下載從UI線程中推出很方便。 (我並不需要進度跟蹤,具有諷刺意味的是,Thread比「便利對象」AsyncTask更容易實現。)有趣的是,當從一個Thread對象調用getInputStream()流時,我沒有看到頭。

唯一的區別是,在一種情況下,我從AsyncTask派生的對象中發出請求,另一種情況是我從Thread派生的對象中發出請求。

當我使用來自AsyncTask的HttpUrlConnection時,我在輸入流中看到標題。當我使用線程中的HttpUrlConnection時,我只看到響應主體。根據文件,後者是「正確的」行爲。

我試過切換方法。也就是說,我在一個線程而不是一個AsyncTask中創建我的第一個請求。如果我這樣做,我沒有看到標題。當我在AsyncTask而不是一個線程中發出第二個請求時,我會在輸入流中看到只有正文需要的頭文件。所以這個問題似乎與我是在一個AsyncTask還是在一個線程中有關。

很明顯,我可以選擇一種或其他方法讓我的HTTP活動脫離UI線程,但我想了解發生了什麼,所以我可以使其行爲相同,無論它如何調用。我喜歡使用AsyncTask,因爲它具有簡單的內置進度追蹤機制,但這是不正確的方法。

想法?建議?

更多信息

我已經封裝在一個小功能,看起來像這樣(實際的代碼必須將其傳送接收到的字符串委託我HttpURLConnection的訪問,但我已經簡化它的測試):

/** 
* Reads the data at the URL. 
* @param urlString The URL to read 
*/ 
public void Get(
    String urlString) 
    { 
    URL url; 
    BufferedInputStream in; 
    try 
     { 
     url = new URL(urlString); 
     conn = (HttpURLConnection) url.openConnection(); 
     conn.setRequestMethod("GET"); 
     conn.setRequestProperty("Accept", "text/*"); 
     in = new BufferedInputStream(conn.getInputStream(), 8192); 

     byte [] buffer = new byte[8192]; 
     StringBuilder stringBuffer = new StringBuilder(16384); 

     int read; 
     while ((read = in.read(buffer)) != -1) 
      { 
      String tempstr = new String(buffer, 0, read, "ISO-8859-1"); 
      System.out.println(tempstr); 
      } 
     return true; 
     } 
    catch (IOException e) 
     { 
     return false; 
     } 

我從兩個地方調用這個函數。爲了測試,我有兩個地方要求從我們的服務器的同一頁。首先是一個擴展AsyncTask的對象。在doInBackground()中,我實例化包含Get()的對象,然後調用Get()。其次是擴展Thread的對象,其中我實例化包含Get()的對象,然後在run()中調用Get()。在這兩種情況下,我都請求相同的頁面。

結果是當我從我的AsyncTask調用時,我看到所有的響應標題。我實際看到的是原始HTTP - 包括傳輸編碼「分塊」時的塊長度。當我從我的線程調用時,我只能得到HTML的正文,這正是我期望的。

爲了測試,我在服務器上寫了一個腳本,它返回原始請求。這是我得到(注意 「awebsite」 是不是我的服務器的名稱):

在一個線程在運行時:

Connection: Keep-Alive 
Accept: text/* 
Host: www.awebsite.com 
User-Agent: Java0 

當在的AsyncTask運行:

[CRLF] 
HTTP/1.1 200 OK 
Date: Tue, 31 May 2011 23:29:20 GMT 
Server: Microsoft-IIS/6.0 
X-Powered-By: ASP.NET 
Content-Type: text/html 
Set-Cookie: ASPSESSIONIDCQSTTQAQ=OHBIFGJCPEAAHGNHICPKOKFO; path=/ 
Cache-control: private 
Transfer-Encoding: chunked 
[CRLF] 
53 
Connection: Keep-Alive 
Accept: text/* 
Host: www.awebsite.com 
User-Agent: Java0 
[CRLF] 
0 
[CRLF] 

注意這兩種情況似乎都發出了同樣的要求。然而,在我從AsyncTask中調用Get()的情況下,某些東西會欺騙HttpURLConnection,使其與正文一起給出響應標頭。

若要進一步混淆的東西,訪問www.google.com按預期工作 - 我從來沒有看到頭與身體混合。所以這似乎表明服務器端有問題(這讓我想知道服務器如何知道失敗),或者有一些關於服務器響應的問題,它會在AsyncTask中運行時混淆HttpURLConnection。

+0

我面對同樣的情況。我發送Thread和SOMETIMES的請求,所有頭文件都在getInputStream()中。也在尋找解決方法。 – Fedor 2011-06-29 01:41:28

+0

@Chirag,你能告訴我爲什麼要使用標題。我必須從android驗證一些憑據我在xammp上使用php。我該如何去做。因爲我不知道如何編寫帶有標題的php代碼 – 2015-02-17 08:04:41

回答

3

我希望在你的服務器端生成一個錯誤:也許有一個響應頭有幾個嵌入的換行符,並且http解析器將它作爲頭的結尾。

+0

服務器如何知道我是從一個線程還是一個AsyncTask請求頁面?無論哪種情況,數據都是一樣的(好,壞,畸形,不管)。 – Craig 2011-05-31 15:02:16

+0

我覺得費米是對的。在這兩種情況下,你確定要求服務器提供同樣的東西嗎?我從來沒有遇到過這樣的問題,即響應包含您所描述的標題。嘗試請求公共領域的東西,看看你是否也從他們那裏得到標題(使用微軟,谷歌,雅虎,蘋果......嘗試所有:)) – WarrenFaith 2011-05-31 15:33:57

+0

嗯,服務器無法知道你是否在一個線程或一個AsyncTask中,所以你必須要求不同的東西,或者不正確地使用流水線或Keepalive之類的東西。粘貼您正在使用的2個代碼塊。 – Femi 2011-05-31 15:58:32

0
  1. 檢查conn.getResponseCode()。如果它是-1,你不應該嘗試讀取流。
  2. 如果您在2.1或更低版本,請執行以下操作:System.setProperty(「http.keepAlive」,「false」);
    此處更多詳細信息HttpURLConnection.getResponseCode() returns -1 on second invocation
+0

的PHP代碼,請告訴我爲什麼要使用標題。我必須從android驗證一些憑據我在xammp上使用php。我該如何去做。因爲我不知道如何編寫帶有頭文件的php代碼 – 2015-02-17 08:05:17