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

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

En cierto punto, recibo un error de falta 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; } 

Obtuve esta forma de cambiar el tamaño para ahorrar espacio con los Documentos de Android: cargando mapas de bits grandes de manera eficiente

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

 return BitmapFactory.decodeFile(resId, options); 

—– editar —– Aquí es cómo estoy agregando cada artículo a FrameLayout.

 for(int ps=0;ps<productSplit.size();ps++){ //split each product by the equals sign List 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 usar otra configuración de bitmap 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 bitmap. Esto ocupa mucha memoria – imageize X 4. Así que si el tamaño de la imagen es de 4 megapíxeles, 16 megabytes se asignarán inmediatamente en el montón, agotando rápidamente la memoria.

En su lugar, use RGB_565, que hasta cierto punto deteriora la calidad, pero para compensar esto puede oscurecer las imágenes.

Entonces, para su método decodeSampledBitmapFromResource, agregue los siguientes fragmentos:

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

En tu 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

Los dispositivos de alta resolución, como S4, generalmente se quedan sin memoria si no tiene su imagen en la carpeta apropiada que es drawable-xxhdpi . También puedes poner tu imagen en drawable-nodpi . La razón por la que se quedaría sin más si tu imagen fuera dibujable es 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 qué tan bien cambie el tamaño o cuánta memoria tiene el dispositivo, en cierto punto se le acabará 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 generalmente es un ListView , un GridView o un ViewPager , al reciclar las vistas, ViewPager usar el diseño y puedo disponer de las nuevas imágenes necesarias.

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

Todavía necesitará administrar la memoria de bitmap ya que no trataría de asignar un espacio total de más de 3 veces el tamaño de la pantalla (si lo considera tiene sentido para el comportamiento de desplazamiento). Si superpone una imagen encima de otra, en algún momento, está alcanzando un error de falta de memoria. Es posible que deba considerar capturar la imagen de la pantalla anterior como una imagen de fondo única para asegurarse de que aún se ajusta a la memoria disponible. O cuando una imagen nueva se superpone a una imagen existente, solo cargue y represente la parte visible. Si el rendimiento se convierte en un problema, es posible que deba considerar las Texturas OpenGL, pero el problema de asignación de memoria sigue siendo el mismo.

Revise todo el Entrenamiento de Visualización de Mapas de Bits, ya que debería brindarle algunas ideas adicionales sobre cómo manejar la visualización.

Usar la biblioteca de Fresco para cargar imágenes grandes evitará este error. en el diseño xml

  

y en javacode

 Uri uri = Uri.parse("http://sofes.miximages.com/android/image.png"); SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view); draweeView.setImageURI(uri);