Meine Android-App fordert Informationen von unserem Server mit einem HttpURLConnection-Objekt an. Der Server gibt einen Text zurück - je nach Anforderung kann das ein wenig (~50 Zeichen) oder viel (12 MB) sein. Der Mime-Typ ist text/plain.
Bei meiner ersten Implementierung musste ich enttäuscht feststellen, dass getInputStream() die gesamte HTTP-Antwort des Servers zurückgibt - einschließlich der Header -, obwohl die Dokumentation besagt, dass sie nur den Antwortkörper enthält. Nun gut... Ich implementierte einen kleinen Zustandsautomaten, um die Header zu lesen und zu speichern, und stellte in meinem Code Schnittstellen bereit, die es dem Aufrufer ermöglichen, auf Header-Felder zuzugreifen. Ich rief sie von einer AsyncTask aus auf, um sie vom UI-Thread zu lösen und eine einfache Methode zur Fortschrittsverfolgung zu erhalten. Alles war gut.
Ich habe vor kurzem eine weitere Funktion hinzugefügt, die Informationen vom Server anfordert, also habe ich dasselbe "http-Verbindungs-Wrapper"-Objekt verwendet, das erwartet, Header im Body-Stream zu sehen. Dieses Mal war es praktisch, ein Thread-Objekt zu verwenden, um den Download aus dem UI-Thread herauszulösen. (Ich brauchte die Fortschrittsverfolgung nicht und ironischerweise ist Thread einfacher zu implementieren als das "Komfortobjekt" AsyncTask). Interessanterweise sah ich die Header im getInputStream()-Stream nicht, wenn er von einem Thread-Objekt aufgerufen wurde.
Der einzige Unterschied besteht darin, dass ich in einem Fall die Anforderung aus einem von AsyncTask abgeleiteten Objekt und im anderen Fall die Anforderung aus einem von Thread abgeleiteten Objekt stelle.
Wenn ich HttpUrlConnection von einer AsyncTask verwende, sehe ich Header im Eingabestrom. Wenn ich HttpUrlConnection von einem Thread aus verwende, sehe ich nur den Antwortkörper. Letzteres ist das "richtige" Verhalten gemäß der Dokumentation.
Ich habe versucht, die Methoden zu wechseln. Das heißt, ich mache meine erste Anfrage in einem Thread statt in einer AsyncTask. Wenn ich das tue, sehe ich die Kopfzeilen nicht. Wenn ich meine zweite Anfrage in einem AsyncTask statt in einem Thread stelle, sehe ich die Kopfzeilen im Eingabestrom, wo nur der Körper erwartet wird. Das Problem scheint also damit zusammenzuhängen, ob ich mich in einer AsyncTask oder in einem Thread befinde.
Natürlich könnte ich die eine oder andere Methode wählen, um meine HTTP-Aktivität aus dem UI-Thread herauszuholen, aber ich möchte verstehen, was vor sich geht, damit ich dafür sorgen kann, dass sie sich unabhängig von der Art des Aufrufs identisch verhält. Und ich mag die Verwendung von AsyncTask, weil es eine einfache integrierte Fortschrittsverfolgung Mechanismus hat, aber das ist die Methode, die nicht richtig funktioniert.
Ideen? Vorschläge?
MEHR INFORMATIONEN
Ich habe meinen HttpURLConnection-Zugriff in einer kleinen Funktion gekapselt, die wie folgt aussieht (der eigentliche Code hat einen Delegaten, an den er die empfangene Zeichenfolge sendet, aber ich habe es zum Testen vereinfacht):
/**
* 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;
}
Ich rufe diese Funktion von zwei Stellen aus auf. Zum Testen rufe ich an beiden Stellen dieselbe Seite von unserem Server ab. Die erste ist in einem Objekt, das AsyncTask erweitert. In doInBackground() instanziere ich das Objekt, das Get() enthält, und rufe dann Get() auf. Zweitens in einem Objekt, das Thread erweitert, wo ich das Objekt, das Get() enthält, instanziiere und dann Get() in run() aufrufe. In beiden Fällen rufe ich die gleiche Seite auf.
Das Ergebnis ist, wenn ich von meinem AsyncTask aufrufen, sehe ich ALLE Antwort-Header. Was ich tatsächlich sehe, ist das rohe HTTP - einschließlich der Chunk-Längen, wenn Transfer-Encoding "chunked" ist. Wenn ich von meinem Thread aus aufrufe, erhalte ich nur den HTML-Body, was ich auch erwarte.
Zum Testen habe ich ein Skript auf dem Server geschrieben, das die Rohanfrage zurückgibt. Das ist das Ergebnis (beachten Sie, dass "awebsite" nicht der Name meines Servers ist):
Bei Ausführung in einem Thread:
Connection: Keep-Alive
Accept: text/*
Host: www.awebsite.com
User-Agent: Java0
Bei Ausführung in einer 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]
Beachten Sie, dass in beiden Fällen anscheinend dieselbe Anfrage gesendet wird. Allerdings ist etwas Tricksen HttpURLConnection in mir die Antwort-Header rechts zusammen mit dem Körper in dem Fall, wo ich Get() von einem AsyncTask aufrufen.
Um die Dinge noch weiter zu verwirren, funktioniert der Zugriff auf www.google.com wie erwartet - ich sehe nie Kopfzeilen mit dem Textkörper vermischt. Das scheint also darauf hinzuweisen, dass entweder etwas falsch auf der Serverseite (was mich fragen lässt, wie der Server weiß, um zu scheitern) oder es gibt etwas darüber, wie der Server antwortet, dass HttpURLConnection verwirrt, wenn es in einer AsyncTask ausgeführt wird.