¿Cómo cargar imágenes grandes en Android y evitar el error de memoria?

Estoy trabajando en una aplicación que utiliza imágenes grandes (1390 × 870: 150kb – 50kb). Estoy agregando imágenes mientras toco un disparador / ImageView.

En un cierto punto estoy recibiendo un error de memoria:

java.lang.OutOfMemoryError E/AndroidRuntime(23369): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) E/AndroidRuntime(23369): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:613) E/AndroidRuntime(23369): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:378) 

Para cambiar el tamaño de la imagen estoy haciendo esto:

 Bitmap productIndex = null; final String imageLoc = IMAGE_LOCATION; InputStream imageStream; try { imageStream = new FileInputStream(imageLoc); productIndex = decodeSampledBitmapFromResource(getResources(), imageLoc, 400, 400); productIV.setImageBitmap(productIndex); } catch (FileNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } public static Bitmap decodeSampledBitmapFromResource(Resources res, String resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(resId, options); } 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 / 3; final int halfWidth = width / 3; // 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; } 

Conseguí esta manera de cambiar de tamaño para ahorrar espacio de los Docs de Android: Cargando mapas de bits grandes de manera eficiente

De acuerdo con el registro de este como es el culpable en el método decodeSampledBitmapFromResource :

 return BitmapFactory.decodeFile(resId, options); 

—– editar —– Aquí es cómo estoy añadiendo cada elemento a la FrameLayout.

 for(int ps=0;ps<productSplit.size();ps++){ //split each product by the equals sign List<String> productItem = Arrays.asList(productSplit.get(ps).split("=")); String tempCarID = productItem.get(0); tempCarID = tempCarID.replace(" ", ""); if(String.valueOf(carID).equals(tempCarID)){ ImageView productIV = new ImageView(Configurator.this); LayoutParams productParams = new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); productIV.setId(Integer.parseInt(partIdsList.get(x))); productIV.setLayoutParams(productParams); final String imageLoc = productItem.get(2); InputStream imageStream; try { imageStream = new FileInputStream(imageLoc); productIndex = decodeSampledBitmapFromResource(getResources(), imageLoc, 400, 400); productIV.setImageBitmap(productIndex); } catch (FileNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } productLayers.addView(productIV); } } 

Puede utilizar otro bitmap-config para disminuir considerablemente el tamaño de las imágenes. El valor predeterminado es RGB-config ARGB8888, lo que significa que se utilizan cuatro canales de 8 bits (rojo, verde, azul, alhpa). Alpha es la transparencia del mapa de bits. Esto ocupa una gran cantidad de memoria – imagesize X 4. Así que si el tamaño de la imagen es de 4 megapíxeles 16 megabytes se asignará inmediatamente en el montón – agotando rápidamente la memoria.

En su lugar, use RGB_565 que, en cierta medida, deteriora la calidad, pero para compensar esto puede ajustar las imágenes.

Así – a su método decodeSampledBitmapFromResource – agregue los fragmentos siguientes:

  options.inPreferredConfig = Config.RGB_565; options.inDither = true; 

En su código:

  public static Bitmap decodeSampledBitmapFromResource(Resources res, String resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; options.inPreferredConfig = Config.RGB_565; options.inDither = true; return BitmapFactory.decodeFile(resId, options); } 

Referencias:

http://developer.android.com/reference/android/graphics/Bitmap.Config.html#ARGB_8888

Dispositivos de alta resolución como S4 por lo general se quedan sin memoria si no tiene su imagen en la carpeta adecuada que es drawable-xxhdpi . También puede poner su imagen en drawable-nodpi . La razón por la que se quedaría sin memorey si su imagen sólo en drawable que el android escalaría la imagen pensando que la imagen fue diseñada para baja resolución.

Here is how I'm adding each item to the FrameLayout que es el problema, el código sigue agregando y agregando más imágenes, y no importa cómo usted cambia de tamaño o cuánto la memoria el dispositivo tiene, en cierto punto que se agotará memoria. Esto se debe a que cada imagen que agregas guarda en la memoria.

Para este tipo de situación lo que hacen las aplicaciones es usar un ViewGroup que pueda reciclar vistas. No conozco su diseño, pero suele ser un ListView , GridView o ViewPager , al reciclar las vistas, vuelve a usar el diseño y puede disponer de volver a cargar imágenes según sea necesario.

Para el propósito específico de cargar y de cambiar el tamaño de las imágenes recomiendo encarecidamente el uso de la biblioteca de Picasso ya que está MUY bien escrito, fácil de usar y estable.

Usted todavía va a tener que gestionar la memoria de mapa de bits, ya que no intentar asignar un espacio total de más de 3 veces el tamaño de la pantalla (si lo piensa tiene sentido para el comportamiento de desplazamiento). Si está superponiendo una imagen encima de otra, en algún momento, está golpeando un error de memoria perdida. Es posible que necesite buscar la captura de la imagen de la pantalla anterior como una sola imagen de fondo para asegurarse de que todavía encaja dentro de la memoria disponible. O cuando una nueva imagen se superpone a una imagen existente sólo carga y procesa la parte visible. Si el rendimiento se convierte en un problema, es posible que deba considerar OpenGL Textures pero el problema de asignación de memoria sigue siendo el mismo.

Ir a través de todos los de visualización de Bitmaps de formación, ya que debería darle algunas ideas adicionales de cómo manejar la pantalla.

Utilizar la biblioteca de Fresco para cargar imágenes grandes evitará este error. En formato xml

 <com.facebook.drawee.view.SimpleDraweeView android:id="@+id/my_image_view" android:layout_width="1300dp" android:layout_height="1300dp" fresco:placeholderImage="@drawable/my_drawable" /> 

Y en javacode

 Uri uri = Uri.parse("https://image.png"); SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view); draweeView.setImageURI(uri); 
  • ¿Por qué tanta memoria?
  • OutOfMemoryError en BitmapFactory.decodeFile ()
  • Error de memoria al cargar mapas de bits
  • Java.lang.OutOfMemoryError Incluso en un bloque try-catch?
  • El tamaño del mapa de bits supera el error de presupuesto de Vm android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.