2009-10-27 6 views
13

Per la mia applicazione corrente raccolgo immagini da diversi "eventi provider" in Spagna.Android: Bug con ThreadSafeClientConnManager download delle immagini

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; 

Tuttavia, quando si scaricano le immagini dalla salir.com ottengo il seguente output logcat:

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 

Una ricerca di quel messaggio di errore non ha fornito risultati molto utili.

Qualcuno ha un'idea di quale potrebbe essere il problema?

Gracias!


Update 1:

Dopo aver contattato un po 'di più e prova cose diverse ho capito che il problema sembra risiedere da qualche altra parte. Anche se la mia uscita logcat dice

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 

che è la corretta lunghezza dell'immagine in byte sembra che ci sia qualcosa che non va con la corrente o la connessione HTTP.

Il mio codice originale (sopra) sta sfruttando il ThreadSafeClientConnManager. Se lo sostituisco con un semplice URLConnection funziona perfettamente:

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

Quindi, mi chiedo ora perché il mio lavoro in modo impeccabile ThreadSafeClientConnManager (almeno sembra lo fa) con tutti i miei altri collegamenti (per lo scambio di JSONObjects) ma non con le immagini di alcuni siti Web specifici (ad es. salir.com - per la maggior parte degli altri siti Web funziona, tuttavia). C'è un parametro HTTP che mi manca?

La mia messa a punto corrente è:

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

Aggiornamento 2:

Ora, la cosa strana è, che in realtà funziona con la ThreadSafeClientConnManager-sometimes-. Se continuo a provare a scaricare l'immagine e a decodificarla per un paio di volte di seguito, potrebbe funzionare dopo 15-30 tentativi. Molto strano.

Spero ci sia una soluzione a questo poiché preferirei usare lo ThreadSafeClientConnManager invece di URLConnection.


Update 3:

Come suggeriscono da Mike Mosher sotto, sembra che utilizzando BufferedHttpEntity l'errore decodifica non compare più. Tuttavia ora, anche se meno spesso di prima, ottengo un errore SkImageDecoder::Factory returned null.

risposta

0

Stavo riscontrando un problema simile e il problema di root era dovuto ai timeout durante la richiesta di alcune immagini. Sono passato a utilizzare uno Background Image Loader e da allora non ho avuto problemi. Spero che questo aiuti

+0

Il timeout non sembra essere il problema per me. Come già pubblicato nel mio output logcat, ho recuperato con successo la mia immagine. – znq

+0

Ok, dopo aver fatto qualche altro test sembra che ThreadSafeClientConnManager sia il problema. Tuttavia, non so perché. – znq

+0

Credo che questa risposta faccia riferimento a questo link: http://russenreaktor.wordpress.com/2009/08/11/android-imageloader-load-images-sequencially-in-the-background/ – gregm

27

Stefan,

Ho avuto lo stesso problema, e non ha trovato molta ricerca su Internet. Molte persone hanno avuto questo problema, ma non molte risposte per risolverlo.

Stavo recuperando immagini usando URLConnection, ma ho scoperto che il problema non si trova nel download, ma il BitmapFactory.decodeStream aveva un problema che decodificava l'immagine.

Ho modificato il mio codice per riflettere il codice originale (utilizzando httpRequest). Ho apportato una modifica, che ho trovato a http://groups.google.com/group/android-developers/browse_thread/thread/171b8bf35dbbed96/c3ec5f45436ceec8?lnk=raot (grazie Nilesh). È necessario aggiungere "BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity (entità);"

qui era il mio codice precedente:

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

E il suo è il codice che funziona:

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

Come ho detto , Ho una pagina che scarica circa 40 immagini, e puoi aggiornare per vedere le foto più recenti. Sarei quasi mezzo fallito con la "decodifica-> decodifica restituita errore falso". Con il codice sopra, non ho avuto problemi.

Grazie

+0

Great! Grazie mille Mike. Sembra che sia così. Dopo due mesi e mezzo ho finalmente una soluzione :-) * molto felice * – znq

+0

Ok, dopo aver fatto un po 'più di test ora non sembra funzionare come sembrava prima. A volte capisco: "SkImageDecoder :: Factory restituito null" - tuttavia, meno spesso di prima. – znq

+0

SkImageDecoder :: Factory restituirà null se non comprende il formato dell'immagine. Questo è un errore completamente diverso quando decodifica-> decodifica viene eseguita dopo che SkImageDecoder :: Factory ha determinato il tipo di immagine. Si verificherà quando l'immagine non è un formato supportato o se non è affatto un'immagine (ad esempio una pagina di errore 404) ... – seanhodges

0

ho provato albero modi diffrent ora, il semplice URLConnection si utilizza e che sembra funzionare per voi, il modo in cui Mike Mosher usa e anche in questo modo [http://asantoso.wordpress.com/2008/03/07/download-and-view-image-from-the-web/] tutti risultato nella decodifica restituiti falsa .

TUTTAVIA se converto l'immagine in PNG tutti i modi funzionano bene !! Comunque ho provato a mettere le immagini sulla mia homepage ftp del mio lavoro e poi tutti i jpg si caricano bene con la soluzione semplice ... Quindi per qualche ragione sembra che se il server non è abbastanza veloce analizza i file jpg prima che siano completamente scaricati o qualcosa.

5

La mia soluzione non è quella di utilizzare BitmapFactory.decodeStream() perché, a mio parere, utilizza il decodificatore SKIA e talvolta sembra un po 'irregolare. Puoi provare qualcosa come questo.

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 (......){ 

    }   

e per la funzione di copia()

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

e IO_BUFFER_SIZE è un intero costante con il valore di 4 * 1024.

0

avevo lo stesso problema quando si cerca di decodificare un immagine da una matrice di byte. Dopo alcuni esperimenti, la soluzione sembra essere quella di assegnare un po 'di memoria temporanea nelle Opzioni di BitmapFactory. Prova:

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

Se il problema non viene risolto subito, provare ad aumentare la dimensione della matrice temperatura. Penso che i file bitmap di grandi dimensioni necessitino di un buffer più grande per la decodifica.

+0

Gracias. Lo proverò e fornirò un feedback quando sarà testato estesamente. – znq

0

L'impostazione del buffer predefinito in Opzioni aiuta a risolvere alcuni problemi di memoria relativi a BitmapFactory, ma certamente non li copre tutti.Il problema sottostante sembra essere una sorta di timeout durante il recupero della bitmap o dell'intestazione della bitmap. Ora sto scrivendo lo stream su un file temporaneo e poi passo il file temporaneo a BitmapFactory che sta funzionando bene.

+0

L'utilizzo dell'approccio BufferedHttpEntity risolve anche il problema di timeout di BitmapFactory. Tuttavia, memorizza in cache l'intero download in memoria, raddoppiando sostanzialmente i requisiti di memoria per la decodifica delle immagini. Questo potrebbe spiegare la fabbrica occasionale restituito errori nulli. –

2

Prova anche ad aggiungere questa opzione.

opt.inPurgeable = true; 
0
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; 
      } 
     } 
+0

Piuttosto che pubblicare solo un blocco di codice, per favore * spiega * perché questo codice risolve il problema posto. Senza una spiegazione, questa non è una risposta. –

2
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); 
+0

Spiegare cosa hai fatto nel tuo codice e perché risolve il problema dell'OP rende sempre la tua risposta migliore, anche se la soluzione sembra semplice. – GraphicsMuncher

3

Questo è un bug di Android. Il tuo codice è ok "I decodificatori di Android attualmente non supportano dati parziali sulla decodifica." Se hai un'immagine nell'entità probabilmente è un flusso di input parziale, e Android non può farcela al momento.

soluzione è quella di utilizzare FlushedInputStream da questa discussione: http://code.google.com/p/android/issues/detail?id=6066

+0

In realtà voglio togliere il mio voto positivo per questo. Sembra che BufferedHttpEntity sia ciò che funziona per me. Penso che il bug che si collega si applica solo alle versioni precedenti dell'SDK. – JamesD

0

sperimento questo errore “SkImageDecoder :: fabbrica restituito nulla” circa 1 su 8 volte, anche se scaricare l'intero file in un buffer, quindi decodificare l'array di byte in una bitmap. Ho provato quanto segue e nessuno ha funzionato per me:

  • Usando solo gli URL, e non i tipi di connessione HTTP
  • lettori Buffered
  • Scarica intero file, quindi eseguire decodificare

Non ci credo funzionerebbe anche la soluzione di flusso dello stream di input. Sembra un bug skia Android. Le persone segnalano ancora problemi qui: http://code.google.com/p/android/issues/detail?id=6066. Non ho soluzioni per questo errore, tranne per riprovare se la bitmap è nullo, il che riduce solo le possibilità di ottenere l'errore.