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
, поэтому вы не копируете накопленную строку на каждой итерации.
вы пробовали встроенный метод для преобразования потока в строку? Это часть библиотеки Apache Commons (org.Апачи.общее.Ио.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
возможно, несколько быстрее, чем ответ Хайме Сориано, и без многобайтовых проблем кодирования ответа Адриана, я предлагаю:
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: оказывается, что если вам не нужно повторно кодировать контент (т. е. вы хотите, чтобы контент КАК) вы не должны использовать ни один из подклассов Reader. Просто используйте соответствующий поток производный класс.
замените начало предыдущего метода соответствующими строками следующего, чтобы ускорить его дополнительно 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 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);
полное
Я использую для чтения полных данных:
// inputStream is one instance InputStream
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
String dataString = new String(data);