Android Чтение из входного потока эффективно - PullRequest
143 голосов
/ 22 марта 2010

Я отправляю 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

Ответы [ 12 ]

338 голосов
/ 31 марта 2010

Проблема в вашем коде заключается в том, что он создает множество тяжелых 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, поэтому вы не копируете накопленную строку на каждой итерации.
32 голосов
/ 24 августа 2010

Вы пробовали встроенный метод для преобразования потока в строку? Это часть библиотеки 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

14 голосов
/ 20 апреля 2015

Еще одна возможность с гуавой:

Зависимость: compile 'com.google.guava:guava:11.0.2'

import com.google.common.io.ByteStreams;
...

String total = new String(ByteStreams.toByteArray(inputStream ));
8 голосов
/ 17 августа 2014

Я считаю, что это достаточно эффективно ... Чтобы получить строку из 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.

6 голосов
/ 24 марта 2010

Как насчет этого. Кажется, чтобы дать лучшую производительность.

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 и Мориса Перри

4 голосов
/ 22 марта 2015

Возможно, несколько быстрее, чем ответ Хайме Сориано, и без проблем с многобайтовым кодированием ответа Адриана я предлагаю:

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();
}
3 голосов
/ 24 марта 2010

Возможно, вместо этого читайте «по одной строке за раз» и соединяйте строки, попробуйте «прочитать все доступные», чтобы избежать сканирования конца строки и также избежать объединения строк.

т.е. InputStream.available() и InputStream.read(byte[] b), int offset, int length)

2 голосов
/ 13 января 2014

Чтение одной строки текста за раз и добавление указанной строки к строке по отдельности отнимает много времени как при извлечении каждой строки, так и при обработке большого количества вызовов метода.

Мне удалось повысить производительность, выделив байтовый массив приличного размера для хранения потоковых данных, который при необходимости итеративно заменяется большим массивом, и пытался прочитать столько, сколько мог вместить массив.

По какой-то причине 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();
    }
}

РЕДАКТИРОВАТЬ: Оказывается, что если вам не нужно перекодировать контент (т.е. вы хотите контент КАК ЕСТЬ ), вы не должны использовать какие-либо из подклассов 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];
1 голос
/ 30 августа 2016

Для преобразования InputStream в String мы используем BufferedReader.readLine () метод. Мы выполняем итерацию до тех пор, пока BufferedReader не возвратит ноль, что означает, что больше нет данных для чтения. Каждая строка будет добавлена ​​к StringBuilder и возвращена как 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);

полная

1 голос
/ 30 июня 2016
    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);
...