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.
У меня нет обходного пути для этой ошибки, кроме как повторить попытку, если битмап равен нулю, что уменьшает вероятность получения ошибки.