Autore Topic: GridView drawable e OutOfMemoryError  (Letto 1121 volte)

Offline kobazzo

  • Nuovo arrivato
  • *
  • Post: 12
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    Mac OS X
GridView drawable e OutOfMemoryError
« il: 19 Dicembre 2013, 23:05:54 CET »
0
Un saluto a tutti, ho un problema. La mia Activity è estremamente semplice ma al caricamento delle immagini ho un OutOfMemoryError.
Ho cercato ma delle soluzioni proposte non ho trovato quella che possa fare al caso mio, mi sembrano tutte situazioni più complesse.

Il codice della mia Activity è:
Codice (Java): [Seleziona]
public class Collection_activity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_collection);

        GridView gridView = (GridView) findViewById(R.id.collection_view);
        gridView.setAdapter( new ImageAdapter(this));

    }


    private class ImageAdapter extends BaseAdapter {
        private Context mContext;

        public ImageAdapter(Context c) {
            mContext = c;
        }

        public int getCount() {
            return mThumbIds.length;
        }

        public Object getItem(int position) {
            return null;
        }

        public long getItemId(int position) {
            return 0;
        }

        // create a new ImageView for each item referenced by the Adapter
        public View getView(int position, View convertView, ViewGroup parent) {
            ImageView imageView;
            if (convertView == null) {  // if it's not recycled, initialize some attributes
                imageView = new ImageView(mContext);
                imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                imageView.setPadding(8, 8, 8, 8);
            } else {
                imageView = (ImageView) convertView;
            }

            imageView.setImageResource(mThumbIds[position]);
            return imageView;
        }

        // references to our images
        private Integer[] mThumbIds = {
                R.drawable.collezione_1, R.drawable.collezione_2,
                R.drawable.collezione_3, R.drawable.collezione_3,
                R.drawable.collezione_4, R.drawable.collezione_5,
                R.drawable.collezione_6, R.drawable.collezione_7
        };
    }
}

E il seguente è l'errore restituito:
Codice (Java): [Seleziona]
2993-2993/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.OutOfMemoryError
            at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
            at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:501)
            at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:354)
            at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:785)
            at android.content.res.Resources.loadDrawable(Resources.java:1965)
            at android.content.res.Resources.getDrawable(Resources.java:660)
            at android.widget.ImageView.resolveUri(ImageView.java:616)
            at android.widget.ImageView.setImageResource(ImageView.java:349)
            at it.leonardoderubeis.valevskaya.util.Collection_activity$ImageAdapter.getView(Collection_activity.java:64)
            at android.widget.AbsListView.obtainView(AbsListView.java:2159)
            at android.widget.GridView.makeAndAddView(GridView.java:1341)
            at android.widget.GridView.makeRow(GridView.java:341)
            at android.widget.GridView.fillDown(GridView.java:283)
            at android.widget.GridView.fillFromTop(GridView.java:417)
            at android.widget.GridView.layoutChildren(GridView.java:1229)
            at android.widget.AbsListView.onLayout(AbsListView.java:1994)
            at android.view.View.layout(View.java:14008)
            at android.view.ViewGroup.layout(ViewGroup.java:4373)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
            at android.view.View.layout(View.java:14008)
            at android.view.ViewGroup.layout(ViewGroup.java:4373)
            at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1663)
            at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1521)
            at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
            at android.view.View.layout(View.java:14008)
            at android.view.ViewGroup.layout(ViewGroup.java:4373)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
            at android.view.View.layout(View.java:14008)
            at android.view.ViewGroup.layout(ViewGroup.java:4373)
            at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:1892)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1711)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:989)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4351)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749)
            at android.view.Choreographer.doCallbacks(Choreographer.java:562)
            at android.view.Choreographer.doFrame(Choreographer.java:532)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735)
            at android.os.Handler.handleCallback(Handler.java:725)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:5041)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
            at dalvik.system.NativeStart.main(Native Method)

Sapreste darmi qualche indicazione su come risolvere il problema? Per lo meno quale è la vera natura e quale sia la tecnica più adatta per risolverlo?
Grazie.

Post unito: 20 Dicembre 2013, 00:16:26 CET
.... Forse ho risolto seguendo questa guida Displaying Bitmaps Efficiently | Android Developers

Ora devo capire se mi conviene eseguire il caricamento in un AsyncTask oppure se non occorre.
« Ultima modifica: 20 Dicembre 2013, 00:16:27 CET da kobazzo, Reason: Merged DoublePost »

Offline iceweasel

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 878
  • Respect: +147
    • Mostra profilo
  • Dispositivo Android:
    LGE P990 - Google Nexus 5
  • Sistema operativo:
    Linux Debian Sid
Re:GridView drawable e OutOfMemoryError
« Risposta #1 il: 20 Dicembre 2013, 00:21:24 CET »
0
Il tuo codice contiene un memory leak, crei oggetti senza controllo, non vengono rilasciate le risorge o meglio sbagli a riutilizzare lo stesso oggetto.

Consiglio di leggere con molta attenzione il PDF e se vuoi di vedere anche il video, il trucco è nel utilizzo del tag:

Google I/O 2010
adb logcat | tee /tmp/logcat | grep TAG

Offline kobazzo

  • Nuovo arrivato
  • *
  • Post: 12
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    Mac OS X
Re:GridView drawable e OutOfMemoryError
« Risposta #2 il: 20 Dicembre 2013, 10:33:42 CET »
0
Grazie per le indicazioni. Per ora ho implementato la seguente soluzione, a primo impatto sembra che funzioni.
Ma non so se è la soluzione definitiva o se è necessario implementare anche i Tag (intendi l'holder?) e il task asincrono?

Codice (Java): [Seleziona]
public class Collection_activity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_collection);

        GridView gridView = (GridView) findViewById(R.id.collection_view);
        gridView.setAdapter( new ImageAdapter(this));

    }


    private class ImageAdapter extends BaseAdapter {
        private Context mContext;

        public ImageAdapter(Context c) {
            mContext = c;
        }

        public int getCount() {
            return mThumbIds.length;
        }

        public Object getItem(int position) {
            return null;
        }

        public long getItemId(int position) {
            return 0;
        }

        // create a new ImageView for each item referenced by the Adapter
        public View getView(int position, View convertView, ViewGroup parent) {
            ImageView imageView;
            if (convertView == null) {  // if it's not recycled, initialize some attributes
                imageView = new ImageView(mContext);
                imageView.setLayoutParams(new GridView.LayoutParams(185, 200));
                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                imageView.setPadding(28, 8, 8, 8);
            } else {
                imageView = (ImageView) convertView;
            }

            imageView.setImageBitmap(
                    decodeSampledBitmapFromResource( getResources(),mThumbIds[position], 100, 100 ) );
            return imageView;
        }

        // references to our images
        private Integer[] mThumbIds = {
                R.drawable.collezione_1, R.drawable.collezione_2,
                R.drawable.collezione_3, R.drawable.collezione_4,
                R.drawable.collezione_5, R.drawable.collezione_6,
                R.drawable.collezione_7, R.drawable.collezione_1
        };

        public int calculateInSampleSize(BitmapFactory.Options options,
                                                int reqWidth, int reqHeight) {
            // Raw height and width of image
            final int height = options.outHeight;
            final int width = options.outWidth;
            int inSampleSize = 1;

            if (height > reqHeight || width > reqWidth) {

                // Calculate ratios of height and width to requested height and
                // width
                final int heightRatio = Math.round((float) height
                        / (float) reqHeight);
                final int widthRatio = Math.round((float) width / (float) reqWidth);

                // Choose the smallest ratio as inSampleSize value, this will
                // guarantee
                // a final image with both dimensions larger than or equal to the
                // requested height and width.
                inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
            }

            return inSampleSize;
        }

        public Bitmap decodeSampledBitmapFromResource(Resources res,
                                                             int resId, int reqWidth, int reqHeight) {

            // First decode with inJustDecodeBounds=true to check dimensions
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeResource(res, resId, options);

            // Calculate inSampleSize
            options.inSampleSize = calculateInSampleSize(options, reqWidth,
                    reqHeight);

            // Decode bitmap with inSampleSize set
            options.inJustDecodeBounds = false;
            return BitmapFactory.decodeResource(res, resId, options);
        }
    }

}

Offline iceweasel

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 878
  • Respect: +147
    • Mostra profilo
  • Dispositivo Android:
    LGE P990 - Google Nexus 5
  • Sistema operativo:
    Linux Debian Sid
Re:GridView drawable e OutOfMemoryError
« Risposta #3 il: 20 Dicembre 2013, 21:25:42 CET »
0
Ridimensioni le immagini, hai semplicemente ridotto le singole allocazioni, alla lunga potrebbero saturarti di nuovo la memoria. La cosa migliore, oltre a usare immagini di dimensioni adeguate,  è usare la tecnica consigliata dagli ingegneri di Google, l'uso del holder e dal tag, ricuce di molto il consumo di memoria e rende l'aggiornamento della view molto più veloce. In generale meno oggetti si allocano meglio è.
adb logcat | tee /tmp/logcat | grep TAG

Offline MisterAnt

  • Utente normale
  • ***
  • Post: 272
  • Respect: +4
    • IlSikano
    • Mostra profilo
  • Dispositivo Android:
    Galaxy S4 Active,Galaxy SIII I9300, Galaxy Tab GT-P5100
  • Play Store ID:
    MisterAnt
  • Sistema operativo:
    Ubuntu 12.04/Winzoz 7/Winzoz xp
Re:GridView drawable e OutOfMemoryError
« Risposta #4 il: 21 Dicembre 2013, 09:45:12 CET »
0
Ciao =)
Un consiglio...Prova ad utilizzare le SoftReference che alloccano memoria e le risorse vengono rilasciate senza memory leaks...
Se le zampe del coniglio sono così fortunate, che fine ha fatto il coniglio?

Offline iceweasel

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 878
  • Respect: +147
    • Mostra profilo
  • Dispositivo Android:
    LGE P990 - Google Nexus 5
  • Sistema operativo:
    Linux Debian Sid
Re:GridView drawable e OutOfMemoryError
« Risposta #5 il: 23 Dicembre 2013, 00:07:13 CET »
0
Le ultime versioni di Android hanno la JVM molta aggressiva e le SoftReference usate in altri ambiti (Java di Oracle) non hanno lo stesso comportamento. Sotto Android per le cache è meglio dimenticarsi dei SoftReference e usare LruCache:

LruCache | Android Developers
adb logcat | tee /tmp/logcat | grep TAG