Android: ошибка при загрузке изображений ThreadSafeClientConnManager

Для моего текущего приложения я собираю изображения из разных событий провайдеров "в Испании.

  Bitmap bmp=null;
  HttpGet httpRequest = new HttpGet(strURL);

  long t = System.currentTimeMillis();
  HttpResponse response = (HttpResponse) httpclient.execute(httpRequest);
  Log.i(TAG, "Image ["+ strURL + "] fetched in [" + (System.currentTimeMillis()-t) + "ms]");

     HttpEntity entity = response.getEntity();
     InputStream instream = entity.getContent();
     bmp = BitmapFactory.decodeStream(instream);

     return bmp;

Однако при загрузке изображений с salir.com я получаю следующее logcat output:

13970     Gallery_Activity  I  Fetching image 2/8 URL: http://media.salir.com/_images_/verticales/a/0/1/0/2540-los_inmortales_la_trattoria-marc_aureli_27_29_no.jpg
13970     ServiceHttpRequest  I  Image [http://media.salir.com/_images_/verticales/a/0/1/0/2540-los_inmortales_la_trattoria-marc_aureli_27_29_no.jpg] fetched in [146ms]
13970     skia  D  --- decoder->decode returned false

Поиск этого сообщения об ошибке не принес много полезных результатов.

Кто-нибудь знает, в чем проблема?

Gracias!


Обновление 1:

После изучения немного больше и тестирования различных вещей я понял, что проблема, кажется, лежит где-то в другом месте. Несмотря на то, что мой вывод logcat говорит

13970     ServiceHttpRequest  I  Image [http://media.salir.com/_images_/verticales/a/0/1/0/2540-los_inmortales_la_trattoria-marc_aureli_27_29_no.jpg] fetched in [146ms]
getContentLength(): 93288

которая является правильной длиной изображения в байтах, кажется, что что-то не так с потоком или HTTP-соединением.

В моем исходном коде (выше) используется ThreadSafeClientConnManager. Если я заменил его просто простым URLConnection, он отлично работает:

URL url = new URL(strURL);
URLConnection conn = url.openConnection();
conn.connect();
InputStream instream = conn.getInputStream();
bmp = BitmapFactory.decodeStream(instream);

Итак, теперь мне интересно, почему мой ThreadSafeClientConnManager работает безупречно (по крайней мере, похоже, что он это делает) со всеми моими другими подключениями (в основном обмениваясь JSONObjects), но не с изображениями с некоторых определенных сайтов (например, salir. com - для большинства других веб-сайтов он работает, хотя). Есть ли параметр HTTP, который мне не хватает?

Моя текущая настройка:

HttpParams parameters = new BasicHttpParams();
HttpProtocolParams.setVersion(parameters, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(parameters, HTTP.UTF_8);
HttpProtocolParams.setUseExpectContinue(parameters, false); // some webservers have problems if this is set to true
ConnManagerParams.setMaxTotalConnections(parameters, MAX_TOTAL_CONNECTIONS);
HttpConnectionParams.setConnectionTimeout(parameters, CONNECTION_TIMEOUT);
HttpConnectionParams.setSoTimeout(parameters, SOCKET_TIMEOUT);

SchemeRegistry schReg = new SchemeRegistry();
schReg.register(new Scheme("http", 
     PlainSocketFactory.getSocketFactory(), HTTP_PORT));

ClientConnectionManager conMgr = new ThreadSafeClientConnManager(parameters,schReg);

DefaultHttpClient http_client = new DefaultHttpClient(conMgr, parameters);

Обновление 2:

Теперь, странно, что он действительно работает с ThreadSafeClientConnManager -sometimes -. Если я продолжаю пытаться загрузить изображение и декодировать его пару раз подряд, он может работать после 15-30 испытаний. Очень странно.

Я надеюсь, что там будет решение, так как я предпочитаю использовать ThreadSafeClientConnManager вместо URLConnection.


Обновление 3:

Как показывает Майк Мошер ниже, кажется, что при использовании BufferedHttpEntity ошибка декодирования больше не появляется. Однако теперь, хотя и реже, чем раньше, я получаю ошибку SkImageDecoder::Factory returned null.

Ответы

Ответ 1

Стефан,

У меня была такая же проблема, и я не нашел много поисков в Интернете. У многих людей была эта проблема, но не было ответов на ее решение.

Я получал изображения с использованием URLConnection, но я узнал, что проблема не связана с загрузкой, но у BitmapFactory.decodeStream возникла проблема декодирования изображения.

Я изменил свой код, чтобы отразить исходный код (используя httpRequest). Я сделал одно изменение, которое я нашел в http://groups.google.com/group/android-developers/browse_thread/thread/171b8bf35dbbed96/c3ec5f45436ceec8?lnk=raot (спасибо Nilesh). Вам нужно добавить "BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity (entity);"

Вот мой предыдущий код:

        conn = (HttpURLConnection) bitmapUrl.openConnection(); 
        conn.connect();
        is = conn.getInputStream();
        //bis = new BufferedInputStream(is);
        //bm = BitmapFactory.decodeStream(bis);
        bm = BitmapFactory.decodeStream(is);

И ее код работает:

            HttpGet httpRequest = null;

    try {
        httpRequest = new HttpGet(bitmapUrl.toURI());
    } catch (URISyntaxException e) {
        e.printStackTrace();
    }

    HttpClient httpclient = new DefaultHttpClient();
        HttpResponse response = (HttpResponse) httpclient.execute(httpRequest);

        HttpEntity entity = response.getEntity();
        BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity); 
        InputStream instream = bufHttpEntity.getContent();
        bm = BitmapFactory.decodeStream(instream);

Как я уже сказал, у меня есть страница, загружающая около 40 изображений, и вы можете обновиться, чтобы увидеть самые последние фотографии. Я бы почти наполовину потерпел неудачу, когда "decoder- > decode вернул ложную ошибку". С приведенным выше кодом у меня не было проблем.

Спасибо

Ответ 2

Мое решение состоит не в использовании BitmapFactory.decodeStream(), потому что я смотрю на него, он использует декодер SKIA, и иногда это кажется беспорядочным. Вы можете попробовать что-то вроде этого.

    Bitmap bitmap = null;
    InputStream in = null;
    BufferedOutputStream out = null;
    try {
            in = new BufferedInputStream(new URL(url).openStream(),
                     IO_BUFFER_SIZE);

            final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
            out = new BufferedOutputStream(dataStream, IO_BUFFER_SIZE);
            copy(in, out);
            out.flush();

            final byte[] data = dataStream.toByteArray();
            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);


   } catch (......){

   }        

и для функции copy()

private static void copy(InputStream in, OutputStream out) throws IOException {
    byte[] b = new byte[IO_BUFFER_SIZE];
    int read;
    while ((read = in.read(b)) != -1) {
        out.write(b, 0, read);
    }
}

и IO_BUFFER_SIZE - постоянное целое число со значением 4 * 1024.

Ответ 3

Это ошибка Android. Ваш код в порядке. "Андроидные декодеры в настоящее время не поддерживают частичные данные для декодирования". Если вы получили изображение в сущности, возможно, это частичный поток ввода, и андроид не может справиться с этим на данный момент.

Решение состоит в том, чтобы использовать FlushedInputStream из этого потока: http://code.google.com/p/android/issues/detail?id=6066

Ответ 4

Также попробуйте добавить эту опцию.

opt.inPurgeable = true;

Ответ 5

HttpGet httpRequest = null;
try {
  httpRequest = new HttpGet(url);
} catch (Exception e) {
  e.printStackTrace();
}
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = (HttpResponse) httpclient.execute(httpRequest);
HttpEntity entity = response.getEntity();
BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity); 
InputStream instream = bufHttpEntity.getContent();
Bitmap bm = BitmapFactory.decodeStream(instream);

Ответ 6

У меня была аналогичная проблема, и проблема с корнем была вызвана таймаутами при запросе некоторых изображений. Я переключился на Загрузчик фонового изображения и с тех пор не имел никаких проблем. Надеюсь, что это поможет

Ответ 7

Теперь я попробовал разные пути, простой URLConnection, который вы используете, и который, кажется, работает на вас, как Майк Мошер использует, а также таким образом [http://asantoso.wordpress.com/2008/03/07/download-and-view-image-from-the-web/], все они приводят к тому, что декодирование возвращает false.

ОДНАКО, если я конвертирую изображение в PNG, все способы работают нормально!! Однако я попытался разместить изображения на своей домашней странице ftp, а затем все jpg загрузились с помощью простого решения... Поэтому по какой-то причине кажется, что если сервер не достаточно быстрый, он анализирует файлы jpg, прежде чем они будут полностью загружены или что-то в этом роде.

Ответ 8

У меня была такая же проблема при попытке декодирования изображения из массива байтов. После некоторых экспериментов решение состоит в том, чтобы назначить некоторое временное хранилище в опциях BitmapFactory. Попробуйте:

Options options = new Options();
options.inTempStorage = new byte[256];
Bitmap newMapBitmap = BitmapFactory.decodeStream(instream, null, options);

Если проблема не устранена сразу, попробуйте увеличить размер массива temp. Я думаю, что большие файлы растровых изображений нуждаются в большем буфере для декодирования.

Ответ 9

Настройка буфера по умолчанию в параметрах помогает справиться с некоторыми проблемами с памятью, связанными с BitmapFactory, но, конечно же, не охватывает все их. Основная проблема - это некоторый тайм-аут при извлечении растрового изображения или заголовка растрового изображения. Теперь я пишу поток в файл temp, а затем передаю временный файл в BitmapFactory, который работает нормально.

Ответ 10

HttpURLConnection hConn = null;
        hConn = openHttpConnection(szUrl);

        hConn.setRequestMethod("GET");  
        hConn.setRequestProperty("User-Agent",szUserAgent);
        hConn.setRequestProperty("Accept",szAccept);
        hConn.setRequestProperty("Accept-Charset",szCharset);

        hConn.setInstanceFollowRedirects(true);
        hConn.setUseCaches(true);
        hConn.setChunkedStreamingMode(8*1024);
        hConn.setDoInput(true);
        hConn.setConnectTimeout(60*1000);
        hConn.setReadTimeout(60*1000);
        hConn.connect();


        InputStream bmpIs = hConn.getInputStream();
        BufferedInputStream bmpBis = new BufferedInputStream(bmpIs); 
        Bitmap bmpThumb = null;

        BitmapFactory.Options bfOpt = new BitmapFactory.Options();

        bfOpt.inScaled = true;
        bfOpt.inSampleSize = 2;
        bfOpt.inPurgeable = true;

        bmpThumb = BitmapFactory.decodeStream(bmpBis,null,bfOpt);

        if(bmpThumb == null)
        {
            for(int i=0; i<10; i++)
            {
                Thread.sleep(200);
                System.gc();

                bmpThumb = BitmapFactory.decodeStream(bmpBis,null,bfOpt);


                if(bmpThumb == null)
                    bfOpt.inSampleSize += 1;
                else
                    break;
            }
        }

Ответ 11

Я испытываю эту ошибку "SkImageDecoder:: Factory возвращает null" около 1 из 8 раз, даже если я загружаю весь файл в буфер, а затем декодирует массив байтов в растровое изображение. Я попробовал следующее, и никто не работал у меня:

  • Использование только URL-адресов, а не типов http-соединений
  • Буферизованные считыватели
  • Загрузить весь файл, затем декодировать

Я не верю, что работа с обводненными входными потоками будет работать. Кажется, это ошибка Android skia. Люди все еще сообщают о проблемах здесь:  http://code.google.com/p/android/issues/detail?id=6066. У меня нет обходного пути для этой ошибки, кроме как повторить попытку, если битмап равен нулю, что уменьшает вероятность получения ошибки.