2013-02-03 16 views
6

Ho ViewPager che uso per mostrare immagini zoomabili (utilizzando ImageViewTouch). Ho bisogno di caricare bitmap di grandi dimensioni da Internet (http). Per grande intendo 2000x1000. Le immagini devono essere così grandi, perché sono zoomabili e devono mostrare i dettagli. Le immagini sul server sono in formato .jpg, ma non è un problema - posso cambiarlo.Caricamento di bitmap di grandi dimensioni su ImageView in ViewPager - esaurimento della memoria

Come posso caricare immagini di grandi dimensioni su ImageViewTouch (ImageView) senza ottenere i proble con la memoria?

Ormai sto usando semplicemente questo (AsyncTask):

ImageView currentView; //ImageView where to place image loaded from Internet 
    String ImageUrl; //URL of image to load 

    protected Bitmap doInBackground(ArrayList... params) { 

      Bitmap bitmap; 
      InputStream in = null; 
      imageUrl = (String)params[0].get(1); 

      try{ 
       HttpClient httpclient = new DefaultHttpClient(); 
       HttpResponse response = httpclient.execute(new HttpGet(imageUrl)); 
       in = response.getEntity().getContent(); 
      } catch(Exception e){ 
       e.printStackTrace(); 
      } 
      try { 
       bitmapa = BitmapFactory.decodeStream(in); 
       in.close(); 
      } catch (IOException e1) { 
       e1.printStackTrace(); 
      } 

      return bitmap; 
     } 


     @Override 
     protected void onPostExecute(Bitmap bitmap) { 

      currentView.setImageBitmap(bitmap); 
     } 

Ed causare molti problemi con la memoria:

E/dalvikvm-heap(369): 69560740-byte external allocation too large for this process. 
E/GraphicsJNI(369): VM won't let us allocate 69560740 bytes 

o

E/AndroidRuntime(369): java.lang.RuntimeException: An error occured while executing doInBackground() 
E/AndroidRuntime(369): at android.os.AsyncTask$3.done(AsyncTask.java:200) 
E/AndroidRuntime(369): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274) 
E/AndroidRuntime(369): at java.util.concurrent.FutureTask.setException(FutureTask.java:125) 
E/AndroidRuntime(369): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308) 
E/AndroidRuntime(369): at java.util.concurrent.FutureTask.run(FutureTask.java:138) 
E/AndroidRuntime(369): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088) 
E/AndroidRuntime(369): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581) 
E/AndroidRuntime(369): at java.lang.Thread.run(Thread.java:1019) 
E/AndroidRuntime(369): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget 
E/AndroidRuntime(369): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) 
E/AndroidRuntime(369): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:470) 
E/AndroidRuntime(369): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:525) 
E/AndroidRuntime(369): at com.package.app.ImageDownloader.doInBackground(ImageDownloader.java:78) 
E/AndroidRuntime(369): at com.package.app.ImageDownloader.doInBackground(ImageDownloader.java:1) 
E/AndroidRuntime(369): at android.os.AsyncTask$2.call(AsyncTask.java:185) 
E/AndroidRuntime(369): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306) 
E/AndroidRuntime(369): ... 4 more 
+0

http: // StackOverflow. it/a/24135283/294884 – Fattie

risposta

2

Quale versione del OS test su questo? Nelle versioni precedenti lo spazio disponibile sull'heap è molto inferiore rispetto alle versioni successive.

Prenderemo seriamente in considerazione il ridimensionamento dei bitmap per evitare questo. Se stai scaricando un 2000x1000 Bitmap per un telefono mdpi, questa è probabilmente una cattiva idea.

Si consiglia inoltre di leggere this article per ulteriori informazioni su come caricare l'immagine appropriata esattamente per uno specifico ImageView, in base alle sue dimensioni.

Infine, si dovrebbe sempre chiuso un InputStream in un blocco finally:

try { 
     bitmap = BitmapFactory.decodeStream(in); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } finally { 
     if (in != null) { in.close(); } 
    } 

C'è anche android:largeHeap="true" che si può considerare, realizzato appositamente per le applicazioni che si occupano di bitmap di grandi dimensioni, come ad esempio fotoritocco applicazioni.

+0

Grazie per la risposta. La mia versione dell'SDK di destinazione è 15 e il minimo è 4. Ho già letto quell'articolo ma non mi è stato d'aiuto - il problema qui è che non posso ridimensionare o ridimensionare la mia immagine, perché deve rimanere zoomabile e mostrare i dettagli quando ingrandita. – michalsol

+0

Non ti mentirò, non troverai una risposta possibile per contenere un'immagine 2000x1000 in memoria. Pensaci, non importa quello che fai supererai lo spazio disponibile su heap.Forse puoi trovare una strategia diversa simile a come funziona il browser Chrome, eseguire solo una parte dell'immagine in memoria, mentre l'utente esegue una panoramica intorno a te rapidamente ricarica una nuova finestra in base alla finestra corrente. – dnkoutso

+0

Ci ho pensato - ricaricando una parte ingrandita dell'immagine - ma sembra piuttosto difficile da implementare. Avete qualche tutorial su di esso? Grazie! – michalsol

2

Un'immagine di 2000x1000 pixel è grande, ma non così grande.

Vedo, tuttavia, che il processo sta tentando di allocare 69560740 byte prima che muoia, ovvero circa 66MByte!

Il 2000x1000, nel peggiore dei casi, è di circa 2000x1000x4 = 8 MByte, molto meno di 66MByte. Qualcos'altro sta succedendo.

Inoltre, ho trovato un problema con l'utilizzo di Bitmap come valore di ritorno/risultato in AsyncTasks. I task asincroni terminati continuano a conservare il risultato fino a quando non vengono raccolti. Poiché non si controlla la raccolta di garbarge delle istanze AsyncTask, non utilizzare Bitmap come valore di ritorno/risultato. Invece mettere il bitmap in un campo e assegnarla quando il doInBackground sta per finire, e utilizzare questo campo per ottenere il bitmap in OnPostExecute e impostare questo campo su null (!):

Bitmap bitmap; 
protected Void doInBackground(ArrayList... params) { 

     InputStream in = null; 
     imageUrl = (String)params[0].get(1); 

     try{ 
      HttpClient httpclient = new DefaultHttpClient(); 
      HttpResponse response = httpclient.execute(new HttpGet(imageUrl)); 
      in = response.getEntity().getContent(); 
     } catch(Exception e){ 
      e.printStackTrace(); 
     } 
     try { 
      bitmap = BitmapFactory.decodeStream(in); 
      in.close(); 
     } catch (IOException e1) { 
      e1.printStackTrace(); 
     } 

     return null; 
    } 


    @Override 
    protected void onPostExecute(Void unused) { 
     if (bitmap != null) { 
      currentView.setImageBitmap(bitmap); 
     } 

     // And don't forget to null the bitmap field! 
     bitmap = null; 
    } 
+0

Come commento in più alla mia risposta precedente. Penso che il tuo server restituisca immagini molto grandi, perché la VM tenta di allocare 69.500.000 byte. Questa sarebbe un'immagine ARGB_8888 di quasi 3000x3000 pixel. –

+0

Questa soluzione ha migliorato notevolmente l'utilizzo della memoria. Stavo solo facendo quello che hai detto, iniziando con "Inoltre, ho trovato un problema con l'utilizzo di Bitmap come valore di ritorno/risultato in AsyncTasks ......." Ho modificato il mio codice come hai detto e ora posso visualizzare in giro x2 volte più immagini senza crash. Grazie –