Android эффективно читает из входного потока
-
21-09-2019 - |
Вопрос
Я делаю 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 поэтому размер файла не большой.Есть лучший способ сделать это?
Спасибо
Энди
Решение
Проблема в вашем коде в том, что он создает много тяжелых String
Объекты, копируя их содержимое и выполняя операции на них. Вместо этого вы должны использовать StringBuilder
Чтобы не создавать новые String
Объекты в каждом добавлении и избегать копирования массивов ЧАР. Реализация для вашего дела будет чем -то вроде этого:
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder total = new StringBuilder();
for (String line; (line = r.readLine()) != null; ) {
total.append(line).append('\n');
}
Теперь вы можете использовать total
не преобразуя его в String
, но если вам нужен результат как String
, просто добавьте:
String result = 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
Библиотека Apache Commons IO может быть загружена отсюда:http://commons.apache.org/io/download_io.cgi
Еще одна возможность с гуавой:
Зависимость: 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));
}
РЕДАКТИРОВАТЬ: На самом деле такого рода охватывает как 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();
}
Возможно, а затем прочитайте «по одной строке за раз» и присоединяйтесь к струнам, попробуйте «прочитать все доступные», чтобы избежать сканирования на конец линии, а также избежать строковых соединений.
т.е. InputStream.available()
а также InputStream.read(byte[] b), int offset, int length)
Чтение одной строки текста за раз и добавление указанной строки к строке индивидуально занимает много времени как при извлечении каждой строки, так и при накладных расходах на стольких вызовов метода.
Я смог добиться лучшей производительности, выделяя байтологический массив приличного размера для хранения данных потока, и который итеративно заменяется большим массивом, когда это необходимо, и пытаясь прочитать столько, сколько массив.
По какой-то причине Android неоднократно не смог загрузить весь файл, когда в коде использовался inputstream, возвращаемый HttpurlConnection, поэтому мне пришлось прибегнуть к использованию как буферизованщика, так и механизма времени ожидания рук, чтобы убедиться, что я буду получить весь файл или отменить перевод.
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();
}
}
РЕДАКТИРОВАТЬ: Оказывается, если вам не нужно перекодировать контент (т.е. вам нужен контент КАК ЕСТЬ) Вы не должны использовать ни одного из подклассов читателя. Просто используйте соответствующий подкласс потока.
Замените начало предыдущего метода соответствующими линиями следующего, чтобы ускорить его дополнительные от 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, мы используем БуфередЧтение.readLine() метод.Мы повторяем до тех пор, пока БуферизованныйReader верните ноль, что означает, что больше нет данных для чтения.Каждая строка будет добавлена к Строитель строк и вернулся как 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);