2012-08-13 18 views
16

Ho un'immagine di 5000 x 4000 px che voglio disegnare su una tela.Caricamento di immagini grandi senza OutOfMemoryError

Prima ho provato a caricarlo dalle risorse. L'ho messo in /res/drawable.

ho usato il seguente metodo:

InputStream input = getResources().openRawResource(R.drawable.huge_image); 
Drawable d = Drawable.createFromStream(input, "image"); 
d.setBounds(...); 
d.draw(canvas); 

Ha funzionato come un fascino.

In questo caso lo InputStream è un AssetManager.AssetInputStream.

Quindi ora voglio caricarlo dalla sdcard.

Qui è quello che ho cercato di fare:

File f = new File(path); 
Uri uri = Uri.fromFile(f); 
InputStream input = mContext.getContentResolver().openInputStream(uri); 
Drawable d = Drawable.createFromStream(input, "image"); 

In questo caso il InputStream è un FileInputStream e ho ricevuto una OutOfMemoryError durante la creazione del Drawable.

Quindi mi chiedo:

C'è un modo per caricare l'immagine senza ottenere quell'errore? O c'è un modo per convertire un FileInputStream in un AssetInputStream?

Nota:

Non voglio per ridimensionare l'immagine, perché sto implementando funzionalità di zoom/pan. Per favore non dirmi di leggere Loading Large Bitmaps Efficiently.

È possibile controllare l'intera classe here. L'errore si verifica quando si utilizza setImageUri().

Ecco il mio Registro errori:

08-13 11:57:54.180: E/AndroidRuntime(23763): FATAL EXCEPTION: main 
08-13 11:57:54.180: E/AndroidRuntime(23763): java.lang.OutOfMemoryError: bitmap size exceeds VM budget 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:468) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:332) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.graphics.drawable.Drawable.createFromStream(Drawable.java:657) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at com.benitobertoli.largeimagezoom.ZoomImageView.setDrawablefromUri(ZoomImageView.java:187) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at com.benitobertoli.largeimagezoom.ZoomImageView.setImageUri(ZoomImageView.java:588) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at com.benitobertoli.largeimagezoom.TestActivity.onKeyDown(TestActivity.java:30) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.view.KeyEvent.dispatch(KeyEvent.java:1257) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.app.Activity.dispatchKeyEvent(Activity.java:2075) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1673) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.view.ViewRoot.deliverKeyEventToViewHierarchy(ViewRoot.java:2493) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2463) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.view.ViewRoot.handleMessage(ViewRoot.java:1752) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.os.Handler.dispatchMessage(Handler.java:99) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.os.Looper.loop(Looper.java:144) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.app.ActivityThread.main(ActivityThread.java:4937) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at java.lang.reflect.Method.invokeNative(Native Method) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at java.lang.reflect.Method.invoke(Method.java:521) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at dalvik.system.NativeStart.main(Native Method) 

EDIT:

stavo testando il mio codice su un A8181 HTC Desire. Dopo aver saputo che il primo frammento di codice non funzionava su altri dispositivi, ho provato su un Samsung Galaxy S2 e sull'emulatore.

Risultati: Quando si carica dalle risorse, l'emulatore ha dato un OutOfMemoryError, il Galaxy S2 non ha un'eccezione, ma la tornata Drawable era nullo.

Quindi immagino che per il momento l'unica soluzione sia il downsampling dell'immagine.

+0

Ho provato a eseguire il codice su em em em e riscuote l'errore OutOfMemory anche nel metodo "From Res" :) Quali impostazioni di dispositivo e/o emulatore utilizzate è stato in grado di eseguirlo senza errori? – Joe

risposta

13

Date un'occhiata a:

http://developer.android.com/reference/android/graphics/BitmapFactory.html

decodeStream (InputStream is, Rect outPadding, BitmapFactory.Options opts) 

o

decodeFile (String pathName, BitmapFactory.Options opts) 

questo modo è possibile caricare la vostra intera bitmap e mostrare sullo schermo.

È necessario impostare le opzioni corrette.

http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html

inSampleSize 

ho provato con il tuo codice:

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

options.inSampleSize = 4; 

Bitmap myBitmap = BitmapFactory.decodeFile(mUri.getPath(),options); 
Drawable d = new BitmapDrawable(Resources.getSystem(),myBitmap); 
updateDrawable(d); 

lavori per me.

+0

Come ho detto nella mia domanda, non ho problemi a caricare un'immagine da risorse. Inoltre, non voglio ridimensionare l'immagine. –

+0

mi spiace di averlo provato, quindi provo il metodo decodeStream (InputStream is, Rect outPadding, BitmapFactory.Options) metodo – n3utrino

+0

Ripeto * Non voglio ridimensionare l'immagine *. –

0

Non sono sicuro se sia il modo corretto di farlo, ma ciò può risolvere il problema.

Prova ad aprire l'immagine in una visualizzazione Web. Ciò fornirà funzionalità di zoom in entrata/uscita e di panning integrate e non dovrai preoccuparti di aprire alcun flusso di input o altro.

Per aprire un'immagine in WebView da attività:

WebView wv = (WebView) findViewById(R.id.webView1); 
    wv.loadUrl("file:///android_asset/abc.png"); 

Puoi WebSettings per consentire controlli dello zoom.

Spero che questo ti possa aiutare !!

+0

Grazie per la risposta. Sto sviluppando una visualizzazione personalizzata con rilevamento dei gesti e una visualizzazione Web non è sufficiente. –

3

A mio parere, l'opzione migliore è dividere l'immagine in parti più piccole. Ciò impedirà all'applicazione di fornire OutOfMemoryException. Qui è il mio codice di esempio

prima ottenere l'immagine dalla scheda SD di metterlo in un bitmap

Bitmap b = null; 
     try { 
      File sd = new File(Environment.getExternalStorageDirectory() + link_to_image.jpg); 
      FileInputStream fis; 

      fis = new FileInputStream(sd); 

      BitmapFactory.Options options = new BitmapFactory.Options(); 
      options.inPurgeable = true; 
      options.inScaled = false; 

      b = BitmapFactory.decodeStream(fis, null, options); 
      fis.close(); 

     } catch (FileNotFoundException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

Split bitmap (b) in pezzi più piccoli. In questo caso SPLIT_HEIGHT è 400.

double rest = (image_height + SPLIT_HEIGHT); 
     int i = 0; 
     Bitmap[] imgs = new Bitmap[50]; 

     do { 
      rest -= SPLIT_HEIGHT; 
      if (rest < 0) { 
       // Do nothing 
      } else if (rest < SPLIT_HEIGHT) { 
       imgs[i] = Bitmap.createBitmap(b, 0, (i * SPLIT_HEIGHT), image_width, (int) rest); 
      } else { 
       imgs[i] = Bitmap.createBitmap(b, 0, i * SPLIT_HEIGHT, image_width, SPLIT_HEIGHT); 
      } 

      i++; 

     } while (rest > SPLIT_HEIGHT); 

Ora hai una raccolta di immagini più piccole. Se si vuole farlo in ottima posizione, è possibile riciclare questo bitmap:

// Not needed anymore, free up some memory 
     if (b != null) 
      b.recycle(); 

Da questo punto è possibile inserire le immagini più piccole sulla tela. Nel mio prossimo codice ho messo le immagini in una ScrollView. Il codice per mettere le immagini su una tela non è molto diverso.

// Add images to scrollview 
     for (int j = 0; j < imgs.length; j++) { 
      Bitmap tmp = imgs[j]; 
      if (tmp != null) { 
       // Add to scrollview 
       ImageView iv = new ImageView(activity); 

       String id = j + "" + articlenumber; 
       iv.setId(Integer.parseInt(id)); 
       iv.setTag(i); 

       iv.setImageBitmap(tmp); 

       RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(globalwidth, tmp.getHeight()); 
       lp.addRule(RelativeLayout.BELOW, previousLongPageImageId); 
       lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT); 

       container.addView(iv, lp); 
       previousLongPageImageId = iv.getId(); 
      } 
     } 

Spero che questo ti possa aiutare.

NOTA: la risoluzione massima per le immagini è 2048 * 2048 (restrizioni OpenGL o qualcosa del genere), quindi sarà sempre necessario dividere le immagini in parti più piccole.

+1

Grazie per la risposta. Prima ancora di provare a suddividere la bitmap 'b = BitmapFactory.decodeStream (fis, null, options);' fornisce già un OutOfMemoryError. Inoltre, stai dividendo la bitmap in 50 più piccole, ma le stai ancora caricando tutte nello stesso momento in memoria, il che dà lo stesso risultato. –

+0

Se si imposta android: largeHeap = "true" e quindi si esegue il codice sopra? Come ho detto nella mia nota, dovrai dividere l'immagine in pezzi perché la risoluzione massima è 2048 * 2048. Ovviamente nella mia soluzione dovrai anche dividere l'immagine in pezzi verticali (la larghezza è più alta del 2048). – Ceetn

0

Se ha funzionato quando si carica da risorse e ora non funziona quando si carica manualmente, ho il sospetto che si abbia qualche problema con la gestione delle risorse, probabilmente qualche perdita di memoria.

OutOfMemoryError si verifica sempre all'avvio dell'applicazione o si verifica in un secondo momento? È in modifica alla configurazione? Usi campi statici (se usato in modo errato questo spesso porta a perdite di memoria nell'applicazione Android, example with explanation questo si adatta al tuo caso)?
Prova a utilizzare alcuni strumenti di creazione profili (google può aiutare a trovare qualcosa di utile).

+0

'OutOfMemoryError' si verifica su questa riga' Disegnabile d = Drawable.createFromStream (input, "image"); 'quando carico da Uri. Non sto usando nessun campo statico. Puoi consultare l'intera classe [qui] (https://github.com/BenitoBertoli/ZoomImageView/blob/master/src/com/benitobertoli/largeimagezoom/ZoomImageView.java). Sto chiamando 'setImageUri()'. –

+0

questo collegamento è per codice di lavoro (immagine caricata da risorse). A quanto pare non hai spinto i cambiamenti problematici (nuovo ramo?). –

+0

Si prega di controllare di nuovo ora. Ho aggiunto un menu. "Da Res" e "Da file". L'errore si verifica utilizzando "Da file". Devi copiare 'huge_image.png' da' res/drawable' alla tua sdcard e sostituire il percorso nel tuo codice. –

Problemi correlati