Чтение Android из потока ввода эффективно

Я делаю запрос HTTP на веб-сайт для приложения Android, которое я делаю.

Я использую DefaultHttpClient и используя HttpGet для запроса. Я получаю ответ объекта и получаю объект InputStream для получения html страницы.

Затем я повторяю ответ следующим образом:

BufferedReader r = new BufferedReader(new InputStreamReader(inputStream)); String x = ""; x = r.readLine(); String total = ""; while(x!= null){ total += x; x = r.readLine(); } 

Однако это ужасно медленно.

Это неэффективно? Я не загружаю большую веб-страницу – www.cokezone.co.uk, поэтому размер файла невелик . Есть лучший способ сделать это?

благодаря

Энди

Solutions Collecting From Web of "Чтение Android из потока ввода эффективно"

Проблема в вашем коде заключается в том, что он создает множество тяжелых объектов String , копирует их содержимое и выполняет на них операции. Вместо этого вы должны использовать StringBuilder чтобы избежать создания новых объектов String в каждом добавлении и избегать копирования массивов char. Реализация для вашего дела будет примерно такой:

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

Теперь вы можете использовать total не преобразовывая его в String , но если вам нужен результат как String , просто добавьте:

Строковый результат = total.toString ();

Я постараюсь объяснить это лучше …

  • a += b (или a = a + b ), где a и b являются строками, копирует содержимое как a и b в новый объект (обратите внимание, что вы также копируете a , который содержит накопленную String ), и вы Делают эти копии на каждой итерации.
  • a.append(b) , где a является StringBuilder , непосредственно добавляет b содержимое в a , поэтому вы не копируете накопленную строку на каждой итерации.

Вы пробовали встроенный метод для преобразования потока в строку? Это часть библиотеки Apache Commons (org.apache.commons.io.IOUtils).

Тогда ваш код будет такой:

 String total = IOUtils.toString(inputStream); 

Документацию по этому вопросу можно найти здесь: http://commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html#toString%28java.io.InputStream%29

Библиотека IO Apache Commons можно скачать здесь: http://commons.apache.org/io/download_io.cgi

Еще одна возможность с Гуавой:

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

 import com.google.common.io.ByteStreams; ... String total = new String(ByteStreams.toByteArray(inputStream )); 

Как насчет этого. Кажется, это дает лучшую производительность.

 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)); } 

Редактировать: На самом деле этот тип охватывает как стальные банки, так и Морис Перри

Я считаю, что это достаточно эффективно … Чтобы получить строку из InputStream, я бы назвал следующий метод:

 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(); } 

Я всегда использую UTF-8. Разумеется, вы могли бы задать набор символов в качестве аргумента, помимо InputStream.

Возможно, несколько быстрее, чем ответ Хайме Сориано, и без многобайтовых проблем кодирования ответа Адриана я предлагаю:

 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(); } 

Возможно, вместо того, чтобы читать «по одной строке за раз» и присоединяться к строкам, попробуйте «прочитать все доступные», чтобы избежать сканирования для конца строки, а также избегать объединения строк.

Т.е. InputStream.available () и InputStream.read (byte [] b, int offset, int length)

Чтение одной строки текста за раз и добавление указанной строки в строку по отдельности занимает много времени как при извлечении каждой строки, так и в издержках стольких вызовов методов.

Я смог получить более высокую производительность, выделив массив байтов с приличным размером для хранения данных потока и который, при необходимости, итеративно заменяется большим массивом и пытается прочитать столько, сколько мог бы хранить массив.

По какой-то причине Android неоднократно не загружал весь файл, когда код использовал InputStream, возвращаемый HTTPUrlConnection, поэтому мне пришлось прибегать к использованию как BufferedReader, так и ручного тайм-аута, чтобы я мог либо получить весь файл, либо отменить перевод.

 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: Оказывается, если вам не нужно повторно кодировать контент (т. Е. Вы хотите, чтобы содержимое AS IS ), вы не должны использовать какой-либо из подклассов Reader. Просто используйте соответствующий подкласс Stream.

Замените начало предыдущего метода соответствующими строками следующего, чтобы ускорить его до 2 – 3 раз .

 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]; 

Если файл длинный, вы можете оптимизировать свой код, добавив в StringBuilder вместо использования конкатенации строк для каждой строки.

  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); 

Чтобы преобразовать InputStream в String, мы используем метод BufferedReader.readLine () . Мы итерации до тех пор, пока BufferedReader не вернет null, что означает, что больше нет данных для чтения. Каждая строка добавляется в StringBuilder и возвращается как String.

  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(); } }` 

И, наконец, из любого класса, где вы хотите преобразовать вызов функции

 String dataString = Utils.convertStreamToString(in); 

полный

Я использую для чтения полные данные:

 // inputStream is one instance InputStream byte[] data = new byte[inputStream.available()]; inputStream.read(data); String dataString = new String(data);