Domanda

Sto per fare una richiesta HTTP get per un sito web per un'applicazione android che sto creando.

Io sto usando una DefaultHttpClient e l'utilizzo di HttpGet di emettere la richiesta.Ho l'entità di risposta e da questo ottenere un oggetto InputStream per ottenere il codice html della pagina.

Poi ho il ciclo attraverso la risposta facendo come segue:

BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
String x = "";
x = r.readLine();
String total = "";

while(x!= null){
total += x;
x = r.readLine();
}

Tuttavia questo è terribilmente lento.

Questo è inefficiente?Io non sono il caricamento di una grande pagina web - www.cokezone.co.uk così la dimensione del file non è grande.C'è un modo migliore per fare questo?

Grazie

Andy

È stato utile?

Soluzione

Il problema nel codice è che si sta creando un sacco di oggetti pesanti String, copiando il contenuto e l'esecuzione di operazioni su di essi. Invece, si dovrebbe utilizzare StringBuilder per evitare la creazione di nuovi oggetti String su ogni aggiungere e per evitare di copiare gli array char. L'implementazione per il vostro caso sarebbe qualcosa di simile a questo:

BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder total = new StringBuilder();
for (String line; (line = r.readLine()) != null; ) {
    total.append(line).append('\n');
}

È ora possibile utilizzare total senza convertirlo in String, ma se avete bisogno il risultato come String, è sufficiente aggiungere:

Risultati String = total.toString ();

Cercherò di spiegare meglio ...

  • a += b (o a = a + b), dove a e b sono stringhe, copia il contenuto di sia a e b a un nuovo oggetto (si noti che si sta anche copiando a , che contiene il accumulato String), e si stanno facendo queste copie ad ogni iterazione.
  • a.append(b), dove a è un StringBuilder, aggiunge direttamente contenuti b a a, in modo da non copiare la stringa accumulato ad ogni iterazione.

Altri suggerimenti

Hai provato il costruito nel metodo per convertire un flusso in una stringa? E 'parte della libreria Apache Commons (org.apache.commons.io.IOUtils).

Allora il vostro codice sarebbe questa una riga:

String total = IOUtils.toString(inputStream);

La documentazione può essere trovato qui: http : //commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html#toString%28java.io.InputStream%29

La libreria Apache Commons IO può essere scaricato da qui: http://commons.apache.org/io/download_io.cgi

Un'altra possibilità con Guava:

dipendenza: compile 'com.google.guava:guava:11.0.2'

import com.google.common.io.ByteStreams;
...

String total = new String(ByteStreams.toByteArray(inputStream ));

Credo che questo sia abbastanza efficiente ... Per ottenere una stringa da un InputStream, lo chiamerei il seguente metodo:

public static String getStringFromInputStream(InputStream stream) throws IOException
{
    int n = 0;
    char[] buffer = new char[1024 * 4];
    InputStreamReader reader = new InputStreamReader(stream, "UTF8");
    StringWriter writer = new StringWriter();
    while (-1 != (n = reader.read(buffer))) writer.write(buffer, 0, n);
    return writer.toString();
}

Io uso sempre UTF-8. Si potrebbe, naturalmente, impostare charset come argomento, oltre InputStream.

Che dire di questo. Sembra dare migliori prestazioni.

byte[] bytes = new byte[1000];

StringBuilder x = new StringBuilder();

int numRead = 0;
while ((numRead = is.read(bytes)) >= 0) {
    x.append(new String(bytes, 0, numRead));
}

Modifica: In realtà questo tipo di comprende sia steelbytes e di Maurice Perry

Forse un po 'più veloce di risposta di Jaime Soriano, e senza i problemi di codifica multi-byte di risposta di Adriano, suggerisco:

File file = new File("/tmp/myfile");
try {
    FileInputStream stream = new FileInputStream(file);

    int count;
    byte[] buffer = new byte[1024];
    ByteArrayOutputStream byteStream =
        new ByteArrayOutputStream(stream.available());

    while (true) {
        count = stream.read(buffer);
        if (count <= 0)
            break;
        byteStream.write(buffer, 0, count);
    }

    String string = byteStream.toString();
    System.out.format("%d bytes: \"%s\"%n", string.length(), string);
} catch (IOException e) {
    e.printStackTrace();
}

Forse piuttosto che leggere 'una riga alla volta' e unire le corde, prova a 'leggere tutte disponibili' in modo da evitare la scansione di fine linea, e per evitare anche stringa unisce.

cioè, InputStream.available() e InputStream.read(byte[] b), int offset, int length)

Leggere una riga di testo alla volta, e aggiungendo detta linea ad una stringa individualmente sia in termini di tempo, sia in estrazione ogni linea e l'overhead di tante invocazioni di metodi.

Sono stato in grado di ottenere migliori prestazioni allocando un array di byte di dimensioni decenti per contenere i dati di flusso, e che è iterativo sostituito con un array più grande, se necessario, e cercando di leggere quanto più la matrice potrebbe contenere.

Per qualche ragione, Android ripetutamente fallito per scaricare l'intero file quando il codice utilizzato InputStream restituito da HttpURLConnection, così ho dovuto ricorrere all'utilizzo sia un BufferedReader e un meccanismo di timeout arrotolato a mano per garantire avrei né ottenere l'intera file o annullare il trasferimento.

private static  final   int         kBufferExpansionSize        = 32 * 1024;
private static  final   int         kBufferInitialSize          = kBufferExpansionSize;
private static  final   int         kMillisecondsFactor         = 1000;
private static  final   int         kNetworkActionPeriod        = 12 * kMillisecondsFactor;

private String loadContentsOfReader(Reader aReader)
{
    BufferedReader  br = null;
    char[]          array = new char[kBufferInitialSize];
    int             bytesRead;
    int             totalLength = 0;
    String          resourceContent = "";
    long            stopTime;
    long            nowTime;

    try
    {
        br = new BufferedReader(aReader);

        nowTime = System.nanoTime();
        stopTime = nowTime + ((long)kNetworkActionPeriod * kMillisecondsFactor * kMillisecondsFactor);
        while(((bytesRead = br.read(array, totalLength, array.length - totalLength)) != -1)
        && (nowTime < stopTime))
        {
            totalLength += bytesRead;
            if(totalLength == array.length)
                array = Arrays.copyOf(array, array.length + kBufferExpansionSize);
            nowTime = System.nanoTime();
        }

        if(bytesRead == -1)
            resourceContent = new String(array, 0, totalLength);
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }

    try
    {
        if(br != null)
            br.close();
    }
    catch(IOException e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Modifica Si scopre che se non c'è bisogno di avere il contenuto ri-codificato (cioè, si desidera che il contenuto di AS IS ) non si dovrebbe utilizzare uno dei sottoclassi Reader. Basta usare la sottoclasse flusso appropriata.

Sostituire l'inizio del metodo precedente con le corrispondenti linee del seguente accelerarlo un extra 2 a 3 volte .

String  loadContentsFromStream(Stream aStream)
{
    BufferedInputStream br = null;
    byte[]              array;
    int                 bytesRead;
    int                 totalLength = 0;
    String              resourceContent;
    long                stopTime;
    long                nowTime;

    resourceContent = "";
    try
    {
        br = new BufferedInputStream(aStream);
        array = new byte[kBufferInitialSize];

Se il file è lungo, è possibile ottimizzare il codice aggiungendo ad uno StringBuilder invece di utilizzare un concatenazione di stringhe per ogni linea.

    byte[] buffer = new byte[1024];  // buffer store for the stream
    int bytes; // bytes returned from read()

    // Keep listening to the InputStream until an exception occurs
    while (true) {
        try {
            // Read from the InputStream
            bytes = mmInStream.read(buffer);

            String TOKEN_ = new String(buffer, "UTF-8");

            String xx = TOKEN_.substring(0, bytes);

Per convertire l'InputStream per Stringa si usa il BufferedReader.readLine() metodo.È scorrere fino alla BufferedReader return null, il che significa che non ci sono più dati da leggere.Ogni linea sarà aggiunto un StringBuilder e restituito come Stringa.

 public static String convertStreamToString(InputStream is) {

        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();

        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sb.toString();
    }
}`

E, infine, dalla classe in cui si desidera convertire chiamare la funzione

String dataString = Utils.convertStreamToString(in);

completa

sto uso per leggere i dati completa:

// inputStream is one instance InputStream
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
String dataString = new String(data);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top