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 136

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

полное

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

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