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