studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones android out-of-memory

para - manual de programacion android pdf



Problema de falta de memoria ImageView problema (8)

Las imágenes vienen en todas las formas y tamaños. En muchos casos, son más grandes que los requeridos para una interfaz de usuario (UI) de aplicación típica. Por ejemplo, la aplicación Galería del sistema muestra fotos tomadas con la cámara de su dispositivo Android, que suelen tener una resolución mucho más alta que la densidad de pantalla de su dispositivo.

Dado que está trabajando con memoria limitada, idealmente solo desea cargar una versión de menor resolución en la memoria. La versión de menor resolución debe coincidir con el tamaño del componente UI que la muestra. Una imagen con una resolución más alta no proporciona ningún beneficio visible, pero aún ocupa una memoria valiosa e incurre en una sobrecarga de rendimiento adicional debido a la ampliación adicional de la escala.

Fuente: carga de grandes mapas de bits de manera eficiente

Según la información anterior, te recomendaría en lugar de configurar la imagen de esta manera:

setImageResource(resId);

para establecerlo así:

setScaledImage(yourImageView, resId);

y Copie y pegue los métodos a continuación:

private void setScaledImage(ImageView imageView, final int resId) { final ImageView iv = imageView; ViewTreeObserver viewTreeObserver = iv.getViewTreeObserver(); viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { public boolean onPreDraw() { iv.getViewTreeObserver().removeOnPreDrawListener(this); int imageViewHeight = iv.getMeasuredHeight(); int imageViewWidth = iv.getMeasuredWidth(); iv.setImageBitmap( decodeSampledBitmapFromResource(getResources(), resId, imageViewWidth, imageViewHeight)); return true; } }); } private static 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); } private static 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) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return inSampleSize; }

Soy nuevo en la programación de Android y recibí un error que dice que a mi aplicación se le acabó la memoria, esto ejemplificó que copié de un libro y funciona con resolución de imágenes pequeña, pero cuando agregué algunas imágenes con una mayor resolución de error de memoria aparece, puedo hacer algo mal o simplemente no sé todo lo que debería trabajar con las imágenes, si alguien sabe qué debo cambiar para que este error no vuelva a aparecer, ruega ayuda. ¡Gracias anticipar!

El código fuente:

public class ImageViewsActivity extends Activity { //the images to display Integer[] imageIDs={ R.drawable.pic1, R.drawable.pic2, R.drawable.pic3, R.drawable.pic4, R.drawable.pic5 }; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final ImageView iv=(ImageView) findViewById(R.id.image1); Gallery gallery=(Gallery) findViewById(R.id.gallery); gallery.setAdapter(new ImageAdapter(this)); gallery.setOnItemClickListener(new OnItemClickListener(){ public void onItemClick(AdapterView<?> parent, View v, int position, long id){ Toast.makeText(getBaseContext(), "pic"+(position+1)+" selected", Toast.LENGTH_SHORT).show(); //display the image selected try{iv.setScaleType(ImageView.ScaleType.FIT_CENTER); iv.setImageResource(imageIDs[position]);}catch(OutOfMemoryError e){ iv.setImageBitmap(null); } } }); } public class ImageAdapter extends BaseAdapter{ private Context context; private int itemBackground; public ImageAdapter(Context c){ context=c; //setting the style TypedArray a = obtainStyledAttributes(R.styleable.Gallery1); itemBackground = a.getResourceId(R.styleable.Gallery1_android_galleryItemBackground, 0); a.recycle(); } //returns the number of images public int getCount() { // TODO Auto-generated method stub return imageIDs.length; } //returns the ID of an item public Object getItem(int position) { // TODO Auto-generated method stub return position; } //returns the ID of an item public long getItemId(int position) { // TODO Auto-generated method stub return position; } //returns an ImageView view public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub ImageView iv= new ImageView(context); iv.setImageResource(imageIDs[position]); iv.setScaleType(ImageView.ScaleType.FIT_XY); iv.setLayoutParams(new Gallery.LayoutParams(150,120)); iv.setBackgroundResource(itemBackground); return iv; } }}

ERROR AQUÍ:

04-18 10:38:31.661: D/dalvikvm(10152): Debugger has detached; object registry had 442 entries 04-18 10:38:31.661: D/AndroidRuntime(10152): Shutting down VM 04-18 10:38:31.661: W/dalvikvm(10152): threadid=1: thread exiting with uncaught exception (group=0x4001d820) 04-18 10:38:31.691: E/AndroidRuntime(10152): FATAL EXCEPTION: main 04-18 10:38:31.691: E/AndroidRuntime(10152): java.lang.OutOfMemoryError: bitmap size exceeds VM budget 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.Bitmap.nativeCreate(Native Method) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.Bitmap.createBitmap(Bitmap.java:499) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.Bitmap.createBitmap(Bitmap.java:466) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:371) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:539) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:508) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:365) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:728) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.content.res.Resources.loadDrawable(Resources.java:1740) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.content.res.Resources.getDrawable(Resources.java:612) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.ImageView.resolveUri(ImageView.java:520) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.ImageView.setImageResource(ImageView.java:305) 04-18 10:38:31.691: E/AndroidRuntime(10152): at image.view.GalleryView$ImageAdapter.getView(GalleryView.java:95) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.Gallery.makeAndAddView(Gallery.java:776) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.Gallery.fillToGalleryLeft(Gallery.java:695) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.Gallery.trackMotionScroll(Gallery.java:406) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.Gallery$FlingRunnable.run(Gallery.java:1397) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.os.Handler.handleCallback(Handler.java:618) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.os.Handler.dispatchMessage(Handler.java:123) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.os.Looper.loop(Looper.java:154) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.app.ActivityThread.main(ActivityThread.java:4668) 04-18 10:38:31.691: E/AndroidRuntime(10152): at java.lang.reflect.Method.invokeNative(Native Method) 04-18 10:38:31.691: E/AndroidRuntime(10152): at java.lang.reflect.Method.invoke(Method.java:552) 04-18 10:38:31.691: E/AndroidRuntime(10152): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:917) 04-18 10:38:31.691: E/AndroidRuntime(10152): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:674) 04-18 10:38:31.691: E/AndroidRuntime(10152): at dalvik.system.NativeStart.main(Native Method)


Google tiene la respuesta correcta (perfecta):

https://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Un ejemplo de cómo lo uso en fragmentos:

private ImageView mImageView; private View view; private int viewWidth; private int viewHeight; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = inflater.inflate(R.layout.fragment_episode_list, container, false); mImageView = (ImageView) view.findViewById(R.id.ImageView); ViewTreeObserver viewTreeObserver = view.getViewTreeObserver(); if (viewTreeObserver.isAlive()) { viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { view.getViewTreeObserver().removeOnGlobalLayoutListener(this); viewWidth = view.getMeasuredWidth(); viewHeight = view.getMeasuredHeight(); mImageView.setImageBitmap(Methods.decodeSampledBitmapFromResource(getResources(), R.drawable.YourImageName, viewWidth, viewHeight)); } }); } return view; }

Puse estos métodos de Google en mi clase "Métodos" (a cualquier otro método útil):

public class Methods { ... public static 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) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; } public static 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); } }


Notas adicionales a la respuesta de @Sakiboy. Aunque probablemente sea demasiado tarde para mi respuesta, pero esta es mi solución, creo que funciona sin la necesidad de hacer muchos cambios de código.

  • Usa Glide para manejar todo el almacenamiento en caché.
  • Para borrar más memoria, debe eliminar manualmente todas las views y establecer cualquier mapa de bits / drawable de ImageView para null y borrar todos los manejadores de eventos y oyentes.
  • Establezca null todas las variables que tiene en su activity o fragment .
  • Debes poner tu lógica dentro de onDestroy y deberías estar listo para continuar.
  • El paso opcional es agregar System.gc() al final de su código.

Después de borrar todo lo que mencioné anteriormente. Notarás que la memoria disminuirá cada vez que se destruya un fragmento / actividad.


Para agregar la respuesta de Ken, que es una pieza sólida de código, pensé que la derribaría después de configurarla:

if(imageView != null) { ((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle(); } imageView = (ImageView) view.findViewById(R.id.imageView); imageView.setImageResource(resID);

NOTA: Esto no funcionará si está tratando de cambiar una imagen que ya recicló. Obtendrás algo como esto en LOGCAT

Lienzo: tratando de usar un mapa de bits reciclado

Entonces, ¿qué hago ahora si no tengo que cargar un montón de imágenes diferentes de forma asíncrona? Simplemente pongo esto en Destroy cuando trato con fragmentos y grandes imágenes de fondo:

@Override public void onDestroy() { super.onDestroy(); imageView.setImageDrawable(null); }


Para aquellos que usan la biblioteca de carga de imágenes Glide , que todavía están teniendo estos OutOfMemory Exception , hay muchas cosas que puede hacer para que Glide utilice menos memoria y, con suerte, solucione su problema. Éstos son algunos de ellos:

  • No use android:scaleType="fitXY" dentro de su ImageView . Entonces, si eres ImageView tiene este aspecto:

    <ImageView android:id="@android:id/icon" android:layout_width="@dimen/width" android:layout_height="@dimen/height" android:adjustViewBounds="true" android:scaleType="fitXY" <!-- DON''T USE "fitXY"! --> />

    Cambie ImageView para usar un android:scaleType diferente android:scaleType , preferiblemente: fitCenter o centerCrop .

  • No use wrap_content en su ImageView , en su lugar use match_parent o especifique el width / height explícitamente usando un tamaño en dp . Si realmente insiste en usar wrap_content en su ImageView , al menos configure un android:maxHeight / android:maxWidth .
  • Desactiva las animaciones con: dontAnimate() en tu Glide.with()...
  • Si está cargando muchas imágenes potencialmente grandes (como lo haría en una lista / cuadrícula), especifique una carga de thumbnail(float sizeMultiplier) en su solicitud. Ex:

    Glide.with(context) .load(imageUri) .thumbnail(0.5f) .dontAnimate() .into(iconImageView);

  • Temporalmente baje la huella de memoria de Glide durante ciertas fases de su aplicación usando: Glide.get(context).setMemoryCategory(MemoryCategory.LOW) .

  • Solo caché en la memoria si es necesario, puede desactivarlo con: skipMemoryCache(true) en su Glide.with()... Esto aún almacenará en caché las imágenes en el disco, lo que probablemente desee ya que está dejando de lado el caché en memoria.
  • Si está cargando un Drawable de sus recursos locales, asegúrese de que la imagen que está tratando de cargar NO ES SUPER ENORME. Hay muchas herramientas de compresión de imágenes disponibles en línea. Estas herramientas reducirán el tamaño de sus imágenes y al mismo tiempo mantendrán la calidad de su apariencia.
  • Si carga desde recursos locales utilice .diskCacheStrategy(DiskCacheStrategy.NONE) .
  • onTrimMemory(int level) devolución de llamada onTrimMemory(int level) que proporciona Android para recortar la caché Glide según sea necesario. Ex.

    @Override public void onTrimMemory(int level) { super.onTrimMemory(level); Glide.get(this).trimMemory(level); }

  • Si muestra imágenes en un RecyclerView puede borrar explícitamente Glide cuando las vistas se reciclan, así:

    @Override public void onViewRecycled(MyAdapter.MyViewHolder holder) { super.onViewRecycled(holder); Glide.clear(holder.imageView); }

  • Si esto sigue ocurriendo, incluso después de haber "intentado todo", el problema podría ser su aplicación (¡GASP!), Y Glide es lo único que lo está empujando a la zona de OutOfMemory Exception ... Así que asegúrese de no usar No tiene pérdidas de memoria en su aplicación. Android Studio proporciona herramientas para identificar problemas de consumo de memoria en tu aplicación.
  • Por último, consulte la página de problemas en Glide''s GitHub , para problemas similares que pueden proporcionar información sobre la solución de su problema (s). El repositorio se maneja muy bien y es muy útil.

Puedes dejarlo en bibliotecas de terceros como Glide

// imageView.setImageResource(imageId); Glide.with(this) // Activity or Fragment .load(imageId) .into(imageView);

A continuación, le indicamos cómo agregarlo a su build.gradle :

compile group: ''com.github.bumptech.glide'', name: ''glide'', version: ''3.7.0''

Picasso de Square también lo hace Picasso carga recursos extraíbles de su URI


Tuve el mismo problema cuando estaba mostrando una imagen grande en la vista de la imagen en el modo PAISAJISMO. así que me resolvieron usando este código

File imgFile = new File(imageFile.getAbsolutePath()); // path of your file FileInputStream fis = null; try { fis = new FileInputStream(imgFile); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 8; options.inPurgeable = true; options.inScaled = true; Bitmap bm = BitmapFactory.decodeStream(fis, null,options); profileIV.setImageBitmap(bm); }


Utilizar

((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle();

Antes de cambiar a una nueva imagen !!