Чтение 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, поэтому размер файла невелик. Есть ли лучший способ сделать это?
Спасибо
Andy
Ответы
Ответ 1
Проблема в вашем коде заключается в том, что он создает множество тяжелых объектов 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
, поэтому вы не копируете накопленную строку на каждой итерации.
Ответ 2
Вы пробовали встроенный метод для преобразования потока в строку? Это часть библиотеки 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
Ответ 3
Еще одна возможность с Guava:
зависимость: compile 'com.google.guava:guava:11.0.2'
import com.google.common.io.ByteStreams;
...
String total = new String(ByteStreams.toByteArray(inputStream ));
Ответ 4
Как насчет этого. Кажется, это дает лучшую производительность.
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: На самом деле этот вид охватывает как стальные банки, так и Maurice Perry
Ответ 5
Я считаю, что это достаточно эффективно... Чтобы получить строку из 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.
Ответ 6
Возможно, несколько быстрее, чем ответ Хайме Сориано, и без многобайтовых проблем кодирования ответа Адриана я предлагаю:
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();
}
Ответ 7
может быть, а затем читать "по одной строке за раз" и присоединяться к строкам, попробовать "прочитать все доступные", чтобы избежать сканирования для конца строки, а также избегать объединения строк.
т.е. InputStream.available() и InputStream.read(byte [] b, int offset, int length)
Ответ 8
Чтение одной строки текста за раз и добавление указанной строки в строку по отдельности требует много времени как для извлечения каждой строки, так и для накладных расходов для многих вызовов методов.
Мне удалось получить лучшую производительность, выделив массив байтов с приличным размером, чтобы хранить данные потока, и при необходимости заменяя его более крупным массивом и пытаясь считывать столько, сколько мог хранить массив.
По какой-то причине 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();
}
}
РЕДАКТИРОВАТЬ: Оказывается, что если вам не нужно повторно кодировать контент (т.е. вы хотите, чтобы содержимое 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];
Ответ 9
Если файл длинный, вы можете оптимизировать свой код, добавив в StringBuilder вместо использования конкатенации строк для каждой строки.
Ответ 10
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);
Ответ 11
Чтобы преобразовать InputStream в String, мы используем BufferedReader.readLine(). Мы итерации до тех пор, пока BufferedReader не возвратит 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);
полный
Ответ 12
Я использую для чтения полные данные:
// inputStream is one instance InputStream
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
String dataString = new String(data);