Je lance une requête HTTP à un site Web pour une application Android que je suis en train de créer.
J'utilise un DefaultHttpClient et utilise HttpGet pour émettre la demande. Je reçois la réponse de l'entité et à partir de là, obtiens un objet InputStream pour obtenir le code HTML de la page.
Je fais ensuite défiler la réponse en procédant comme suit:
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
String x = "";
x = r.readLine();
String total = "";
while(x!= null){
total += x;
x = r.readLine();
}
Cependant, c'est horriblement lent.
Est-ce inefficace? Je ne charge pas une grande page Web - www.cokezone.co.uk donc la taille du fichier n'est pas grande. Y a-t-il une meilleure manière de faire cela?
Merci
Andy
Le problème dans votre code est qu'il crée beaucoup d'objets lourds String
, en copiant leur contenu et en effectuant des opérations dessus. Au lieu de cela, vous devez utiliser StringBuilder
pour éviter de créer de nouveaux objets String
sur chaque ajout et pour éviter de copier les tableaux de caractères. La mise en œuvre pour votre cas ressemblerait à ceci:
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder total = new StringBuilder();
for (String line; (line = r.readLine()) != null; ) {
total.append(line).append('\n');
}
Vous pouvez maintenant utiliser total
sans le convertir en String
, mais si vous avez besoin du résultat en tant que String
, ajoutez simplement:
Résultat de la chaîne = total.toString ();
Je vais essayer de mieux l'expliquer ...
a += b
(ou a = a + b
), où a
et b
sont des chaînes, copie le contenu de les deuxa
etb
vers un nouvel objet (notez que vous copiez également a
, qui contient le accumuléString
), et que vous effectuez ces copies. à chaque itération.a.append(b)
, où a
est un StringBuilder
, ajoute directement le contenu de b
à a
, afin de ne pas copier la chaîne accumulée à chaque itération.Avez-vous essayé la méthode intégrée pour convertir un flux en chaîne? Cela fait partie de la bibliothèque Apache Commons (org.Apache.commons.io.IOUtils).
Ensuite, votre code serait cette ligne:
String total = IOUtils.toString(inputStream);
La documentation correspondante se trouve ici: http://commons.Apache.org/io/api-1.4/org/Apache/commons/io/IOUtils.html#toString%28Java.io.InputStream%29
La bibliothèque Apache Commons IO peut être téléchargée ici: http://commons.Apache.org/io/download_io.cgi
Une autre possibilité avec Goyave:
dépendance: compile 'com.google.guava:guava:11.0.2'
import com.google.common.io.ByteStreams;
...
String total = new String(ByteStreams.toByteArray(inputStream ));
Je pense que cela est assez efficace ... Pour obtenir une chaîne d'un InputStream, j'appellerais la méthode suivante:
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();
}
J'utilise toujours UTF-8. Vous pouvez, bien sûr, définir charset comme argument, en plus de InputStream.
Et ça. Semble donner de meilleures performances.
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 fait, ce genre de comprend à la fois steelbytes et Maurice Perry
Peut-être un peu plus vite que la réponse de Jaime Soriano, et sans les problèmes d'encodage à plusieurs octets de la réponse d'Adrian, je suggère:
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();
}
Peut-être plutôt que de lire "une ligne à la fois" et de joindre les chaînes, d'essayer "read all available" (lire tout disponible) afin d'éviter l'analyse pour la fin de ligne et les jointures de chaînes.
c'est-à-dire InputStream.available()
et InputStream.read(byte[] b), int offset, int length)
Lire une ligne de texte à la fois et ajouter cette ligne à une chaîne individuellement prend du temps, à la fois pour l'extraction de chaque ligne et pour la surcharge de tant d'appels de méthode.
J'ai pu obtenir de meilleures performances en allouant un tableau d'octets de taille décente pour contenir les données de flux. Ce dernier est remplacé de manière itérative par un tableau plus grand en cas de besoin, et en essayant de lire autant que le tableau pourrait en contenir.
Pour une raison quelconque, Android n'a pas réussi à télécharger le fichier entier à plusieurs reprises lorsque le code utilisait InputStream renvoyé par HTTPUrlConnection; récupérer le fichier entier ou annuler le transfert.
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: Il s’avère que si vous n’avez pas besoin de ré-encoder le contenu (c’est-à-dire que vous voulez le contenu TEL QUEL ), vous ne devriez pas N'utilisez aucune des sous-classes de Reader. Utilisez simplement la sous-classe de flux appropriée.
Remplacez le début de la méthode précédente par les lignes suivantes pour l’accélérer extra 2 à 3 fois.
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 le fichier est long, vous pouvez optimiser votre code en ajoutant à StringBuilder au lieu d'utiliser une concaténation String pour chaque ligne.
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);
Pour convertir InputStream en String, nous utilisons la méthode BufferedReader.readLine (). Nous itérons jusqu'à ce que [BufferedReader renvoie null, ce qui signifie qu'il n'y a plus de données à lire. Chaque ligne sera ajoutée à un StringBuilder et renvoyée sous forme de chaîne.
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();
}
}`
Et enfin, depuis n'importe quelle classe où vous voulez convertir, appelez la fonction
String dataString = Utils.convertStreamToString(in);
complet