Android чтение из входного потока эффективно
Я делаю запрос HTTP get на веб-сайт для приложения 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 так что размер файла не большой. Есть ли лучший способ сделать это?
спасибо
Энди
12 ответов:
проблема в вашем коде заключается в том, что он создает много тяжелой
String
объекты, копирование их содержимого и выполнение над ними операций. Вместо этого, вы должны использоватьStringBuilder
чтобы избежать создания новыхString
объекты на каждом добавлении и во избежание копирования массивов символов. Реализация для вашего случая будет что-то вроде этого: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
, просто добавить:результат строки = Итого.toString ();
я постараюсь объяснить это лучше...
a += b
(илиa = a + b
), гдеa
иb
являются строками, копирует содержимое иa
иb
к новому объекту (обратите внимание, что вы также копируетеa
, которая содержит набралString
), и вы делаете эти копии на каждой итерации.a.append(b)
, гдеa
этоStringBuilder
, непосредственно добавляетb
содержимоеa
, поэтому вы не копируете накопленную строку на каждой итерации.
вы пробовали встроенный метод для преобразования потока в строку? Это часть библиотеки Апач Коммонс (орг."Апач".общин.Ио.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
библиотека Apache Commons IO может быть загружена из здесь: http://commons.apache.org/io/download_io.cgi
еще одна возможность с Guava:
зависимость:
compile 'com.google.guava:guava:11.0.2'
import com.google.common.io.ByteStreams; ... String total = new String(ByteStreams.toByteArray(inputStream ));
Я считаю, что это достаточно эффективно... Чтобы получить строку из 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. Вы можете, конечно, установить charset в качестве аргумента, помимо 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)); }
Edit: на самом деле этот вид охватывает как steelbytes, так и Maurice Perry's
возможно, несколько быстрее, чем ответ Хайме Сориано, и без проблем с многобайтовым кодированием ответа Адриана я предлагаю:
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(); }
возможно, вместо того, чтобы читать "по одной строке за раз" и присоединяться к строкам, попробуйте "прочитать все доступные", чтобы избежать сканирования конца строки, а также избежать соединения строк.
ie,
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: получается, что если вам не нужно перекодировать контент (т. е. вы хотите, чтобы контент КАК) вы не должны использовать ни один из подклассов читателя. Просто используйте соответствующий поток производный класс.
замените начало предыдущего метода соответствующими строками следующего, чтобы ускорить его дополнительно от 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);
для преобразования входного потока в строку мы используем BufferedReader.readLine () метод. Мы повторяем до тех пор, пока BufferedReader return null, что означает, что больше нет данных для чтения. Каждая строка будет добавлена к StringBuilder и возвращается в виде строки.
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);
полное