Android Lectura de un flujo de Entrada de manera eficiente
-
21-09-2019 - |
Pregunta
Estoy haciendo una solicitud get HTTP a un sitio web para una aplicación de android que estoy haciendo.
Estoy usando un DefaultHttpClient y el uso de HttpGet para la emisión de la solicitud.Puedo obtener la respuesta de la entidad y de esta obtener un objeto InputStream para obtener el código html de la página.
Yo, a continuación, ciclo a través de la respuesta haciendo de la siguiente manera:
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
String x = "";
x = r.readLine();
String total = "";
while(x!= null){
total += x;
x = r.readLine();
}
Sin embargo, esto es horriblemente lento.
Es este ineficiente?No estoy de carga, una gran página web - www.cokezone.co.uk por lo que el tamaño del archivo no es grande.Hay una manera mejor de hacer esto?
Gracias
Andy
Solución
El problema en tu código es que es la creación de un montón de pesados String
los objetos, con copia de sus contenidos y la realización de operaciones sobre ellos.En su lugar, debe utilizar StringBuilder
para evitar la creación de nuevas String
los objetos en cada anexar y para evitar la copia de el char matrices.La aplicación para tu caso sería algo como esto:
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder total = new StringBuilder();
for (String line; (line = r.readLine()) != null; ) {
total.append(line).append('\n');
}
Ahora puede utilizar total
sin convertirlo a String
, pero si usted necesita el resultado como un String
, simplemente añadir:
String resultado = total.toString();
Voy a tratar de explicarlo mejor...
a += b
(oa = a + b
), dondea
yb
son Cadenas de texto, copia el contenido de ambosa
yb
a un nuevo objeto (tenga en cuenta que usted está copiandoa
, que contiene el acumuladoString
), y que están haciendo esas copias en cada iteración.a.append(b)
, dondea
es unStringBuilder
, directamente anexab
contenidoa
, por lo que no copia el acumulado de cadena en cada iteración.
Otros consejos
¿Usted ha intentado el construido en el método para convertir una corriente a una cadena? Es parte de la biblioteca Apache Commons (org.apache.commons.io.IOUtils).
A continuación, el código sería así de una línea:
String total = IOUtils.toString(inputStream);
La documentación de que se puede encontrar aquí: http : //commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html#toString%28java.io.InputStream%29
La biblioteca de Apache Commons IO se puede descargar desde aquí: http://commons.apache.org/io/download_io.cgi
Otra posibilidad con la guayaba:
dependencia: compile 'com.google.guava:guava:11.0.2'
import com.google.common.io.ByteStreams;
...
String total = new String(ByteStreams.toByteArray(inputStream ));
Creo que esto es lo suficientemente eficiente ... para obtener una cadena de un InputStream, que yo llamaría el método siguiente:
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();
}
Siempre use UTF-8. Podría, por supuesto, set de juego de caracteres como argumento, además de InputStream.
¿Qué hay de esto. Parece dar un mejor rendimiento.
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));
}
Edit: En realidad este tipo de Steelbytes abarca tanto y de Maurice Perry
Posiblemente algo más rápido que la respuesta de Jaime Soriano, y sin la codificación multi-byte problemas de respuesta de Adrian, sugiero:
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();
}
Tal vez en lugar de leer 'una línea a la vez' y unirse a las cuerdas, trate de 'leer todos disponibles' a fin de evitar la exploración de fin de línea, y también para evitar la cadena se une.
es decir, InputStream.available()
y InputStream.read(byte[] b), int offset, int length)
Lectura una línea de texto a la vez, y anexar dicha línea en una cadena individual es mucho tiempo tanto en la extracción de cada línea y la sobrecarga de tantas invocaciones de métodos.
Yo era capaz de conseguir un mejor rendimiento mediante la asignación de una matriz de bytes de tamaño decente para mantener los datos del flujo, y que está iterativa reemplazada con una matriz más grande cuando sea necesario, y tratando de leer tanto como la matriz podía sostener.
Por alguna razón, Android fracasó varias veces para descargar el archivo completo cuando el código utiliza el InputStream devuelto por HttpURLConnection, así que tuve que recurrir al uso tanto de un BufferedReader y un mecanismo de tiempo de espera de enrollado a mano para asegurar Me gustaría obtener ya sea la totalidad archivo o cancelar la transferencia.
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();
}
}
EDIT: Resulta que si usted no necesita tener el contenido re-codificada (es decir, desea que el contenido de COMO ES ) que no debería utilizar cualquiera de las subclases del lector. Sólo tiene que utilizar la subclase corriente adecuada.
Reemplazar el principio del método anterior con las líneas correspondientes de la siguiente para acelerarlo un adicionales 2 a 3 veces .
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];
Si el archivo es largo, puede optimizar su código añadiendo a un StringBuilder en lugar de utilizar una concatenación de cadenas para cada línea.
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);
Para convertir el InputStream a la Cadena que se utiliza el Clases bufferedreader.readLine() método.Repetimos hasta que el Clases bufferedreader retornar null, lo que significa que no hay más datos que leer.Cada línea se anexa a un StringBuilder y se devuelve como una Cadena.
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();
}
}`
Y, finalmente, de cualquier clase en la que desea convertir llamar a la función
String dataString = Utils.convertStreamToString(in);
completa
Soy uso para leer los datos completos:
// inputStream is one instance InputStream
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
String dataString = new String(data);