Android ViewPager con imágenes: pérdida de memoria / fallos de la aplicación

Estoy escribiendo una aplicación que muestra una imagen panorámica que eventualmente tiene varios marcadores en ella para mostrar información sobre ciertos puntos.

Como la gran imagen se estrelló la aplicación (también tengo otra actividad en la aplicación que está mostrando un mapa grande), ahora estoy tratando de mostrar el panorama como una secuencia de páginas con ViewPager.

He logrado mostrar la imagen en 6 bits y pensé que las cosas iban bien, pero ahora la aplicación se bloquea después de unos cuantos swipes (alrededor de 7 a 8) a medida que se agota la memoria.

Estoy tirando de mi cabello por lo que es como pensé que mis artículos se destruirán una vez que están fuera de la pantalla? Soy un absoluto noobs y lo siento si estoy siendo un waster del tiempo. He pasado todo el día leyendo y probando soluciones de aquí y de otros lugares y no soy el más sabio.

aquí está mi código: activity PanoramaView

public class PanoramaView extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.panorama); MyPagerAdapter adapter = new MyPagerAdapter(); ViewPager myPager = (ViewPager) findViewById(R.id.mysixpanelpager); myPager.setAdapter(adapter); myPager.setCurrentItem(2); } } 

MyPagerAdapter

 public class MyPagerAdapter extends PagerAdapter { public int getCount() { return 6; } public Object instantiateItem(View collection, int position) { LayoutInflater inflater = (LayoutInflater) collection.getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); int resId = 0; switch (position) { case 0: resId = R.layout.farleft; break; case 1: resId = R.layout.left; break; case 2: resId = R.layout.middle; break; case 3: resId = R.layout.right; break; case 4: resId = R.layout.farright; break; case 5: resId = R.layout.farfarright; break; } //ImageView imageView = new ImageView(getApplicationContext()); //imageView.findViewById(R.id.imageView); //imageView.setImageBitmap(BitmapFactory.decodeResource(getResources(), ids[position])); View view = inflater.inflate(resId, null); ((ViewPager) collection).addView(view, 0); return view; } @Override public void destroyItem(View collection, int position, Object o) { View view = (View)o; ((ViewPager) collection).removeView(view); view = null; } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == ((View) arg1); } @Override public Parcelable saveState() { return null; } } 

Mi archivo de diseño principal

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <android.support.v4.view.ViewPager android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/mysixpanelpager"/> </LinearLayout> 

Prometo que seré un miembro útil de ahora en adelante (o aún más una vez que realmente sé lo que estoy haciendo).

EDIT: – En la primera actividad, estoy mostrando una imagen que es 552kb. – Las seis imágenes que estoy mostrando en esta actividad (PanoramaView) están entre 309 y 500kb. – He utilizado un rastreador de asignación en Eclipse y todo lo que pude ver fue la memoria de llenado, pero los datos exactos no estaba claro para mí – el accidente ocurre después de mostrar 7 u 8 imágenes (básicamente después de pasar de nuevo y cuarto varias veces)

Aquí está el código de farfarright.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" > <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imageView" android:src="@drawable/panorama6" android:adjustViewBounds="true" android:contentDescription="@string/panorama" > </ImageView> </LinearLayout> 

He intentado fijar el límite de la página de la pantalla que no ayudó.

Encontré este enlace sobre la gestión de la memoria en otro post y lo echaré un vistazo esta noche.

EDIT: aquí está la salida LogCat

 11-28 21:17:42.551: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 51K, 53% free 2558K/5379K, external 2002K/2137K, paused 65ms 11-28 21:17:43.261: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 1K, 53% free 2557K/5379K, external 3297K/4118K, paused 44ms 11-28 21:17:47.741: W/KeyCharacterMap(328): No keyboard for id 0 11-28 21:17:47.741: W/KeyCharacterMap(328): Using default keymap: /system/usr/keychars/qwerty.kcm.bin 11-28 21:17:49.141: D/DFSAPP(328): my button id before is 2 11-28 21:17:49.691: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 36K, 52% free 2614K/5379K, external 15576K/15708K, paused 50ms 11-28 21:17:54.571: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 12K, 52% free 2616K/5379K, external 17386K/17735K, paused 39ms 11-28 21:17:54.661: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 0K, 52% free 2616K/5379K, external 17386K/17735K, paused 61ms 11-28 21:17:54.711: I/dalvikvm-heap(328): Clamp target GC heap from 25.629MB to 24.000MB 11-28 21:17:54.711: D/dalvikvm(328): GC_FOR_MALLOC freed <1K, 52% free 2616K/5379K, external 18975K/21023K, paused 42ms 11-28 21:18:03.751: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 6K, 52% free 2616K/5379K, external 18269K/20317K, paused 46ms 11-28 21:18:03.822: I/dalvikvm-heap(328): Clamp target GC heap from 25.628MB to 24.000MB 11-28 21:18:03.852: D/dalvikvm(328): GC_FOR_MALLOC freed <1K, 52% free 2615K/5379K, external 18975K/20317K, paused 32ms 11-28 21:18:04.131: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed <1K, 52% free 2615K/5379K, external 17386K/19434K, paused 49ms 11-28 21:18:04.191: I/dalvikvm-heap(328): Clamp target GC heap from 25.628MB to 24.000MB 11-28 21:18:04.201: D/dalvikvm(328): GC_FOR_MALLOC freed 0K, 52% free 2615K/5379K, external 18975K/19434K, paused 34ms 11-28 21:18:07.301: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 1K, 52% free 2616K/5379K, external 18269K/19434K, paused 46ms 11-28 21:18:07.381: I/dalvikvm-heap(328): Clamp target GC heap from 25.801MB to 24.000MB 11-28 21:18:07.401: D/dalvikvm(328): GC_FOR_MALLOC freed <1K, 52% free 2616K/5379K, external 19152K/19434K, paused 38ms 11-28 21:18:07.611: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed <1K, 52% free 2615K/5379K, external 18159K/19434K, paused 47ms 11-28 21:18:07.681: I/dalvikvm-heap(328): Clamp target GC heap from 25.801MB to 24.000MB 11-28 21:18:07.681: D/dalvikvm(328): GC_FOR_MALLOC freed 0K, 52% free 2615K/5379K, external 19152K/19434K, paused 36ms 11-28 21:18:18.901: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 5K, 52% free 2616K/5379K, external 18269K/19434K, paused 57ms 11-28 21:18:18.972: I/dalvikvm-heap(328): Clamp target GC heap from 25.802MB to 24.000MB 11-28 21:18:18.991: D/dalvikvm(328): GC_FOR_MALLOC freed <1K, 52% free 2616K/5379K, external 19152K/19434K, paused 33ms 11-28 21:18:19.181: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 1K, 52% free 2615K/5379K, external 18159K/19434K, paused 55ms 11-28 21:18:19.251: I/dalvikvm-heap(328): Clamp target GC heap from 25.801MB to 24.000MB 11-28 21:18:19.251: D/dalvikvm(328): GC_FOR_MALLOC freed 0K, 52% free 2615K/5379K, external 19152K/19434K, paused 33ms 11-28 21:18:21.551: D/dalvikvm(328): GC_EXTERNAL_ALLOC freed 1K, 52% free 2616K/5379K, external 18975K/19434K, paused 46ms 11-28 21:18:21.581: E/dalvikvm-heap(328): 1627200-byte external allocation too large for this process. 11-28 21:18:21.621: I/dalvikvm-heap(328): Clamp target GC heap from 25.629MB to 24.000MB 11-28 21:18:21.621: E/GraphicsJNI(328): VM won't let us allocate 1627200 bytes 11-28 21:18:21.631: D/dalvikvm(328): GC_FOR_MALLOC freed 0K, 52% free 2616K/5379K, external 18975K/19434K, paused 34ms 11-28 21:18:21.641: D/AndroidRuntime(328): Shutting down VM 11-28 21:18:21.641: W/dalvikvm(328): threadid=1: thread exiting with uncaught exception (group=0x40015560) 11-28 21:18:21.732: E/AndroidRuntime(328): FATAL EXCEPTION: main 11-28 21:18:21.732: E/AndroidRuntime(328): android.view.InflateException: Binary XML file line #7: Error inflating class <unknown> 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.createView(LayoutInflater.java:518) 11-28 21:18:21.732: E/AndroidRuntime(328): at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:568) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.rInflate(LayoutInflater.java:623) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.inflate(LayoutInflater.java:408) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.inflate(LayoutInflater.java:320) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.inflate(LayoutInflater.java:276) 11-28 21:18:21.732: E/AndroidRuntime(328): at com.businesbike.dfp.MyPagerAdapter.instantiateItem(MyPagerAdapter.java:43) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:692) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.support.v4.view.ViewPager.populate(ViewPager.java:849) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.support.v4.view.ViewPager.populate(ViewPager.java:772) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.support.v4.view.ViewPager.completeScroll(ViewPager.java:1539) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.support.v4.view.ViewPager.computeScroll(ViewPager.java:1422) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.drawChild(ViewGroup.java:1562) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.drawChild(ViewGroup.java:1644) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.View.draw(View.java:6883) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.widget.FrameLayout.draw(FrameLayout.java:357) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.drawChild(ViewGroup.java:1646) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.drawChild(ViewGroup.java:1644) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.View.draw(View.java:6883) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.widget.FrameLayout.draw(FrameLayout.java:357) 11-28 21:18:21.732: E/AndroidRuntime(328): at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:1862) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewRoot.draw(ViewRoot.java:1522) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewRoot.performTraversals(ViewRoot.java:1258) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.ViewRoot.handleMessage(ViewRoot.java:1859) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.os.Handler.dispatchMessage(Handler.java:99) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.os.Looper.loop(Looper.java:123) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.app.ActivityThread.main(ActivityThread.java:3683) 11-28 21:18:21.732: E/AndroidRuntime(328): at java.lang.reflect.Method.invokeNative(Native Method) 11-28 21:18:21.732: E/AndroidRuntime(328): at java.lang.reflect.Method.invoke(Method.java:507) 11-28 21:18:21.732: E/AndroidRuntime(328): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) 11-28 21:18:21.732: E/AndroidRuntime(328): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) 11-28 21:18:21.732: E/AndroidRuntime(328): at dalvik.system.NativeStart.main(Native Method) 11-28 21:18:21.732: E/AndroidRuntime(328): Caused by: java.lang.reflect.InvocationTargetException 11-28 21:18:21.732: E/AndroidRuntime(328): at java.lang.reflect.Constructor.constructNative(Native Method) 11-28 21:18:21.732: E/AndroidRuntime(328): at java.lang.reflect.Constructor.newInstance(Constructor.java:415) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.view.LayoutInflater.createView(LayoutInflater.java:505) 11-28 21:18:21.732: E/AndroidRuntime(328): ... 36 more 11-28 21:18:21.732: E/AndroidRuntime(328): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget 11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.Bitmap.nativeCreate(Native Method) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.Bitmap.createBitmap(Bitmap.java:477) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.Bitmap.createBitmap(Bitmap.java:444) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:349) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:498) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:473) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:336) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.content.res.Resources.loadDrawable(Resources.java:1709) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.content.res.TypedArray.getDrawable(TypedArray.java:601) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.widget.ImageView.<init>(ImageView.java:118) 11-28 21:18:21.732: E/AndroidRuntime(328): at android.widget.ImageView.<init>(ImageView.java:108) 11-28 21:18:21.732: E/AndroidRuntime(328): ... 39 more 

En caso de que alguien se encuentre con este post, aquí es lo que parece haberlo arreglado: Tengo cambiar el código al código de la respuesta a continuación y también he cortado mi imagen panorámica en bits más pequeños por lo que cada imagen está ahora por debajo de 300kb.

Entonces usa

1

 @Override public Object instantiateItem(ViewGroup collection, int position) @Override public void destroyItem(ViewGroup collection, int position, Object view) 

en lugar de

  public Object instantiateItem(View collection, int position) public void destroyItem(View collection, int position, Object view) 

2 Probado en 6 imágenes tamaño 400k cada trabajo muy bien

 @Override public Object instantiateItem(ViewGroup collection, int position) { LayoutInflater inflater = (LayoutInflater) collection.getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); int resId = 0; switch (position) { case 0: resId = R.layout.farleft; break; case 1: resId = R.layout.left; break; case 2: resId = R.layout.middle; break; case 3: resId = R.layout.right; break; case 4: resId = R.layout.farright; break; case 5: resId = R.layout.farfarright; break; } View view = (View) inflater.inflate(resId, null); //get Image view from layout //ImageView imageView = (ImageView) view.findViewById(resId); //imageView.setImageResource(resId); collection.addView(view, 0); return view; } @Override public void destroyItem(ViewGroup collection, int position, Object view) { collection.removeView((View) view); } 

3 Enlace útil PageAdapter

Buena suerte

Un poco tarde con la respuesta, pero el problema es bastante simple en realidad, y no veo otras respuestas dirigidas a él.

La cosa es, cuando decodifica manualmente los mapas de bits (como lo hace en esas líneas comentadas), tiene que .recycle() ellos mismos . Por lo tanto, de nuevo a su código, en su adaptador tiene que complementar esto:

 @Override public Object instantiateItem(View collection, int position) { // ... View view = inflater.inflate(resId, null); ImageView imageView = (ImageView) view.findViewById(R.id.imageView); imageView.setImageBitmap(BitmapFactory.decodeResource(getResources(), ids[position])); ((ViewPager) collection).addView(view, 0); return view; } 

Con esto: recicle el Bitmap de Bitmap además de eliminar la View de la jerarquía:

 @Override public void destroyItem(View collection, int position, Object o) { View view = (View)o; ImageView imgView = (ImageView) view.findViewById(R.id.imageView); BitmapDrawable bmpDrawable = (BitmapDrawable) imgView.getDrawable(); if (bmpDrawable != null && bmpDrawable.getBitmap() != null) { // This is the important part bmpDrawable.getBitmap().recycle(); } ((ViewPager) collection).removeView(view); view = null; } 

Es tan simple como eso, no hay necesidad de utilizar la gestión de mapa de bits independiente o nada.

Pregunta bien documentada, y aunque parezca haberlo resuelto, aquí hay un poquito de información potencialmente útil.

Desde tu logcat, detecto que estás desarrollando (o por lo menos probando) en Gingerbread (porque Honeycomb en adelante no incluye la parte "externa" en la salida. Esto es importante sólo porque pone de relieve lo que está sucediendo a tus mapas de bits. , los datos de mapa de bits se colocan en la memoria nativa, fuera de la pila.Entonces, un puntero a los datos se coloca dentro de la pila, junto con alguna otra información referencial.La supresión de todas las referencias a la bitmap liberará la información referencial (en algún momento, cuando se System.gc() embargo, los datos de mapa de bits nunca se publicarán, a menos que su dispositivo sea alcanzado por un asteroide – o (menos dramático) que llama al método recycle() en ese mapa de bits. notar, realmente se libera la memoria nativa, por lo que realmente tiene que volver a crear el mapa de bits desde cero cuando es necesario de nuevo por el ViewPager (muy probablemente en el instantiateItem . Honeycomb y adelante colocar los datos de mapa de bits dentro de la pila, que es ligeramente menos anno ying Los data todavía se liberan sólo cuando se recycle() ese mapa de bits (por lo que tienes que cavar un poco más profundo, usando DDMS para determinar lo que está pasando – por ejemplo, estoy actualmente luchando este mismo problema en JB, y en ICS, 4.0.3 se comporta de manera diferente que 4.0.4, pero divago).

Probablemente sea un exceso, pero mi solución fue implementar una clase para rastrear mis mapas de bits y estar seguro de que los había reciclado. En su caso, esto sucedería cuando destroyItem() en ViewPager.

Aquí está la clase (algo peatonal) que uso para rastrear cosas, fwiw.

 public class BitmapManager { private final String TAG = "DEBUG -- " + ClassUtils.getShortClassName(this.getClass()); Context mContext = null; private class BitmapVectorEntry { public Bitmap bm = null; public String name = null; } Vector<BitmapVectorEntry> mBitmapVector = new Vector<BitmapVectorEntry>(); public BitmapManager( Context aContext ) { mContext = aContext; } public void setContext( Context aContext ) { mContext = aContext; } public void registerBitmap( String name, Bitmap b) { if(mBitmapVector == null) { mBitmapVector = new Vector<BitmapVectorEntry>(); } if(b == null) { Log.e(TAG, "Bitmap is NULL!! "); return; } // Log.d(TAG, " ~~~~~~ Registering ["+name+"] ["+b+"]"); BitmapVectorEntry be = new BitmapVectorEntry(); be.bm = b; be.name = name; mBitmapVector.add(be); } public void registerBitmapForBackgroundDrawable( String name, View v ) { if(v != null) { Drawable d = v.getBackground(); if(d != null) { if(d instanceof BitmapDrawable) { Bitmap bm = ((BitmapDrawable) d).getBitmap(); if(bm != null) { // Log.w(TAG, " ~~~~ Registering Background Bitmap [" + bm + "]"); registerBitmap(name, bm); } else { Log.w(TAG, " ~~~~ Background does not have a bitmap in the BitmapDrawable (Probably, but not necessarily, and error)"); } } else { Log.w(TAG, " ~~~~ Background does not have a BitmapDrawable (Might not be an error)"); } } else { Log.w(TAG, " ~~~~~ Background is null, no drawable (Might not be an error)"); } } else { Log.e(TAG, " ~~~~~ View is null, is there no background for this view?"); } } // We cannot recycle certain bitmaps, like the background for the page which houses // the ViewPager, since the pager reuses it, so we just delete it from vector public void clear(Bitmap bm) { removeBitmap(bm, false); } // In most cases, when we are done with a bitmap, we want to recycle it. This is a // synchronous call that frees external heap (in 2.3.x) or internal heap (3.x < ). // And when I say 'synchronous' I mean 'slow' and 'should not be run on the UI Thread, // So be sure to throw this on an async thread public void recycleBitmap(Bitmap bm) { removeBitmap(bm, true); } private void removeBitmap(Bitmap bm, boolean andRecycleToo) { if(bm == null) { Log.e(TAG, "(RECYCLE BITMAP) !!!! Bitmap is NULL!!, cannot recycle"); return; } if(mBitmapVector == null) { Log.e(TAG, "(RECYCLE BITMAP) !!!! Bitmap Vector is NULL!!"); return; } boolean foundIt = false; Bitmap targetBm = null; int i = (mBitmapVector.size() - 1); try { for(; i >= 0; i--) { BitmapVectorEntry b = mBitmapVector.get(i); targetBm = b.bm; if(targetBm.equals(bm)) { foundIt = true; if(andRecycleToo) { if(!targetBm.isRecycled()) { targetBm.recycle(); } } mBitmapVector.removeElementAt(i); // Log.e(TAG, " Recycling ["+targetBm.name+"] ["+targetBm.bm+"]"); break; } } } catch(Exception e) { Log.e(TAG, "Exception during recycling bitmap position ["+i+"] ["+bm+"] ["+e+"]"); } finally { mBitmapVector.trimToSize(); if(andRecycleToo) { if(!foundIt && targetBm != null) { if(!targetBm.isRecycled()) { targetBm.recycle(); } Log.e(TAG, "(RECYCLE BITMAP) ========================= !!! RECYCLING Bitmap ["+targetBm+"], was unregistered, recycled is ["+targetBm.isRecycled()+"]"); } else { // Log.i(TAG, "(RECYCLE BITMAP) ========================= !!! RECYCLING Bitmap ["+targBe.name+"] ["+targBe.bm+"], was registered"); } } } } public void flush() { if(mBitmapVector == null) { // Log.e(TAG, "!!!! Bitmap Vector is NULL!!"); return; } for(int i = 0; i < mBitmapVector.size(); i++) { BitmapVectorEntry bme = mBitmapVector.get(i); if(!bme.bm.isRecycled()) { // Log.e(TAG, "Flushing Bitmap ["+bme.name+"] ["+bme.bm+"]"); bme.bm.recycle(); } } mBitmapVector.clear(); mBitmapVector.trimToSize(); } public void dumpBitmaps() { if(mBitmapVector == null) { // Log.e(TAG, "!!!! Bitmap Vector is NULL!!"); return; } boolean foundOne = false; for(int i = 0; i < mBitmapVector.size(); i++) { Bitmap bm0 = mBitmapVector.get(i).bm; if(!bm0.isRecycled()) { foundOne = true; break; } } if(mBitmapVector.size() > 0 && foundOne) { Log.e(TAG, " ========= Dumping Bitmap Vector === (Found a leaker) ===== "); Log.e(TAG, " "+mBitmapVector.size()+" entries"); for(BitmapVectorEntry b : mBitmapVector) { if(!b.bm.isRecycled()) { Log.e(TAG, " ["+b.name+"] ["+b.bm+"] Recycled ["+b.bm.isRecycled()+"]"); } } Log.e(TAG, " ========= End of Bitmap Dump ======== "); } } } 

El bit de clave es la llamada dumpBitmaps() (al menos para el problema descrito anteriormente). Si diligentemente registra todos los mapas de bits, a continuación, la llamada dumpBitmaps() se expondrá a cualquier que requieren remanente. Si no le importa dónde está la fuga, y sólo quiere que se vaya, entonces usted puede llamar a flush() que eliminará todos los mapas de bits.

Necesitará colocar registerBitmap() dondequiera que cree el mapa de bits. He tenido suerte terrible con el inflater haciendo cosas impredecibles, así que prefiero algo como:

  public Drawable getPreformattedFile() { // Log.d(TAG, "Loading in Drawable ["+preformattedFileName()+"]"); if( preformattedFileName() == null) { Log.e(TAG, "Formatted Filename is null"); return(null); } Drawable ret = null; try { BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inScaled = false; opts.inPurgeable = true; Bitmap bm = BitmapFactory.decodeFile(preformattedFileName(), opts); if(bm == null) { return(null); } mBitmapManager.registerBitmap(mItem.name(), bm); ret = new BitmapDrawable(mContext.getResources(), bm); // Log.i(TAG, " ~~~~~~~~~~~~~~~~~ JUST CHECKING ["+bm+"] ["+((BitmapDrawable) ret).getBitmap()+"]"); } catch( OutOfMemoryError e ) { // Log.e(TAG, " ============== Before gc ==== OOME Thread ["+Thread.currentThread().getName()+"] getPreformattedFile.Before GC Heap Available [[[ "+(Debug.getNativeHeapFreeSize()/1024)+"k ]]]"); System.gc(); // Log.e(TAG, " ============== After gc ==== OOME Thread ["+Thread.currentThread().getName()+"] getPreformattedFile.Before GC Heap Available [[[ "+(Debug.getNativeHeapFreeSize()/1024)+"k ]]]"); e.printStackTrace(); } catch( Exception e) { Log.e(TAG, "Trouble reading PNG file ["+e+"]"); } return(ret); } 

He mencionado un par de veces haciendo las cosas asincrónicamente, y usted mencionó que usted es nuevo-ish a Android. Para completar, debo mencionar que también no me gusta usar AsyncTask, ya que tiene algunas limitaciones muy pesadas en multi-threading, y las imágenes tienden a requerir un montón de multi-threading. Por lo tanto, en su lugar, utilizar un Executor y hacer algo como esto (que, se nota, utilizó el método anterior para hacer el trabajo real):

 public Drawable getPreformattedFileAsync() { if(mItem == null) { Log.e(TAG, " -- ITEM is NULL!!"); return(mErrorDrawable); } if(mFetchFileTask == null) { Log.e(TAG, " -- Task is Null!!, Need to start an executor"); return(mErrorDrawable); } Runnable job = new Runnable() { public void run() { Thread.currentThread().setName("ImagePipeline"); Thread.currentThread().setPriority(Thread.MIN_PRIORITY); Thread.currentThread().yield(); if(mItemDelegate != null) { Drawable retDrawable = getPreformattedFile(); if(showAllDebugInformation) { Log.w(TAG, " ^^^^ Getting preformatted file size ["+retDrawable.getIntrinsicWidth()+"] x ["+retDrawable.getIntrinsicHeight()+"]"); } if(retDrawable != null) { Bitmap bm = ((BitmapDrawable) retDrawable).getBitmap(); // Log.w(TAG, " Size of Bitmap is ["+(bm.getRowBytes()*bm.getHeight())+"]"); mItemDelegate.onDrawableRequest(mItem, retDrawable); if(mBitmapManager != null) { if(mBusyDrawable != null) { mBitmapManager.recycleBitmap(((BitmapDrawable) mBusyDrawable).getBitmap()); } if(mErrorDrawable != null) { mBitmapManager.recycleBitmap(((BitmapDrawable) mErrorDrawable).getBitmap()); } } } else { mItemDelegate.onDrawableRequest(mItem, mErrorDrawable); } } // Log.i(TAG, " RUNNABLE - Set the background"); } }; mImagePipelineTask.execute(job); return(mBusyDrawable); } 

Esto, por supuesto, requiere un Ejecutor:

  private ExecutorService mImagePipelineTask = null; 

Que se puede crear así:

  mImagePipelineTask = Executors.newSingleThreadExecutor(); 

(O, si eres aventurero, puedes usar un ejecutor multi-hilo, misma idea general).

Tal vez esto ayude a aclarar.

 pager.setOffscreenPageLimit(MAX_PAGE); //pager is the ViewPager instance 

aquí MAX_PAGE es el número de páginas que pueden permanecer fuera de la Visibilidad. Cualquier adicional a estos obtendrá destruido y sólo se recrea cuando el usuario vuelve a la posición cercana.

  • Desplazamiento de las imágenes de pantalla en la memoria
  • ¿Cuál es el resto del gráfico principal de Eclipse Memory Analyzer?
  • Cómo salir de error de memoria al iniciar una actividad en la aplicación android?
  • EditText que causa la pérdida de memoria
  • Apertura de la cámara en un proceso diferente
  • Limpiar la memoria del montón para Excepción de memoria perdida
  • ¿Cómo averiguar el uso de memoria de la aplicación en ejecución actual en android?
  • Android - Cómo obtener el uso de memoria de mi dispositivo Android
  • Android: liberación de la memoria asignada AnimationDrawable está utilizando hasta
  • Android: fugas de memoria al crear dinámicamente UI con fondos de recursos de imagen
  • Android: ¿Cómo probar la pérdida de memoria en una aplicación?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.