BitmapFactory.decodeResource devuelve un mapa de bits mutable en Android 2.2 y un mapa de bits inmutable en Android 1.6

Estoy desarrollando una aplicación y probándola en mi dispositivo con Android 2.2. En mi código, hago uso de un mapa de bits que recuperar con BitmapFactory.decodeResource, y puedo hacer cambios llamando bitmap.setPixels() en él. Cuando IllegalStateException esto en el dispositivo de un amigo con Android 1.6, obtengo una IllegalStateException en la llamada a bitmap.setPixels . La documentación en línea dice que se IllegalStateException una IllegalStateException de este método cuando el mapa de bits es inmutable. La documentación no dice nada acerca de decodeResource devolver un mapa de bits inmutable, pero claramente que debe ser el caso.

¿Hay una llamada diferente que puedo hacer para obtener un mapa de bits mutable confiable de un recurso de aplicación sin necesidad de un segundo objeto de Bitmap (podría crear un mutable del mismo tamaño y dibujar en un lienzo, pero que requeriría dos mapas de bits de igual Tamaño utilizando hasta el doble de memoria que yo había previsto)?

Puede convertir su mapa de bits inmutable en un mapa de bits mutable.

Encontré una solución aceptable que utiliza sólo la memoria de un mapa de bits.

Un mapa de bits de origen es raw guardado (RandomAccessFile) en el disco (no memoria RAM), entonces se libera el mapa de bits de origen, (ahora, no hay mapa de bits en memoria) y después de eso, la información de archivo se carga en otro mapa de bits. De esta manera es posible hacer una copia de mapa de bits con sólo un mapa de bits almacenado en memoria ram por tiempo.

Vea la solución completa y la implementación aquí: Android: convierta Immutable Bitmap en Mutable

Agrego una mejora a esta solución, que ahora funciona con cualquier tipo de Bitmaps (ARGB_8888, RGB_565, etc), y elimina el archivo temporal. Ver mi método:

 /** * Converts a immutable bitmap to a mutable bitmap. This operation doesn't allocates * more memory that there is already allocated. * * @param imgIn - Source image. It will be released, and should not be used more * @return a copy of imgIn, but muttable. */ public static Bitmap convertToMutable(Bitmap imgIn) { try { //this is the file going to use temporally to save the bytes. // This file will not be a image, it will store the raw image data. File file = new File(Environment.getExternalStorageDirectory() + File.separator + "temp.tmp"); //Open an RandomAccessFile //Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" //into AndroidManifest.xml file RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); // get the width and height of the source bitmap. int width = imgIn.getWidth(); int height = imgIn.getHeight(); Config type = imgIn.getConfig(); //Copy the byte to the file //Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888; FileChannel channel = randomAccessFile.getChannel(); MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, imgIn.getRowBytes()*height); imgIn.copyPixelsToBuffer(map); //recycle the source bitmap, this will be no longer used. imgIn.recycle(); System.gc();// try to force the bytes from the imgIn to be released //Create a new bitmap to load the bitmap again. Probably the memory will be available. imgIn = Bitmap.createBitmap(width, height, type); map.position(0); //load it back from temporary imgIn.copyPixelsFromBuffer(map); //close the temporary file and channel , then delete that also channel.close(); randomAccessFile.close(); // delete the temp file file.delete(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return imgIn; } 

Usted tiene razón, usando decodeResource obtendrá un mapa de bits inmutable (para probarlo, puede llamar al método isMutable del objeto Bitmap).

Para modificar el mapa de bits, puede utilizar el método de copia para crear una versión mutable de este mapa de bits y, a continuación, modificar píxeles.

 Bitmap immutableBitmap = BitmapFactory.decodeResource(....); Bitmap mutableBitmap = immutableBitmap.copy(Bitmap.Config.ARGB_8888, true); 

Espero que esta información sea útil para usted.

Primero podemos configurar opciones para BitmapFactory instanciando una clase BitmapFactory.Options y luego establecer el campo de opciones llamado 'inMutable' como true y luego pasar esta instancia de opciones a decodeResource.

  BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inMutable = true; Bitmap bp = BitmapFactory.decodeResource(getResources(), R.raw.white, opt); 

He aquí una solución que he creado que utiliza el almacenamiento interno y no requiere ningún permiso nuevo, basado en la idea de "Derzu", y el hecho de que a partir de panal, esto está construido en:

 /**decodes a bitmap from a resource id. returns a mutable bitmap no matter what is the API level.<br/> might use the internal storage in some cases, creating temporary file that will be deleted as soon as it isn't finished*/ @TargetApi(Build.VERSION_CODES.HONEYCOMB) public static Bitmap decodeMutableBitmapFromResourceId(final Context context, final int bitmapResId) { final Options bitmapOptions = new Options(); if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) bitmapOptions.inMutable = true; Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), bitmapResId, bitmapOptions); if (!bitmap.isMutable()) bitmap = convertToMutable(context, bitmap); return bitmap; } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public static Bitmap convertToMutable(final Context context, final Bitmap imgIn) { final int width = imgIn.getWidth(), height = imgIn.getHeight(); final Config type = imgIn.getConfig(); File outputFile = null; final File outputDir = context.getCacheDir(); try { outputFile = File.createTempFile(Long.toString(System.currentTimeMillis()), null, outputDir); outputFile.deleteOnExit(); final RandomAccessFile randomAccessFile = new RandomAccessFile(outputFile, "rw"); final FileChannel channel = randomAccessFile.getChannel(); final MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, imgIn.getRowBytes() * height); imgIn.copyPixelsToBuffer(map); imgIn.recycle(); final Bitmap result = Bitmap.createBitmap(width, height, type); map.position(0); result.copyPixelsFromBuffer(map); channel.close(); randomAccessFile.close(); outputFile.delete(); return result; } catch (final Exception e) { } finally { if (outputFile != null) outputFile.delete(); } return null; } 

Otra alternativa es usar JNI para poner los datos en él, reciclar el mapa de bits original y usar los datos JNI para crear un nuevo mapa de bits, que será (automáticamente) mutable, así que junto con mi solución JNI para mapas de bits , se puede Haz lo siguiente:

 Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher); final JniBitmapHolder bitmapHolder=new JniBitmapHolder(bitmap); bitmap.recycle(); bitmap=bitmapHolder.getBitmapAndFree(); Log.d("DEBUG",""+bitmap.isMutable()); //will return true 

Sin embargo, no estoy seguro de cuál es el requisito mínimo de nivel API. Funciona muy bien en API 8 y superior.

Sé que llego tarde a la fiesta, pero así es como evitamos este doloroso problema de Android, y recorté y modificamos una imagen con sólo una copia en la memoria.

Situación
Queremos procesar los píxeles de una versión recortada de una imagen guardada en un archivo. Con altas demandas de memoria, nunca queremos tener más de una copia de esta imagen en la memoria en un momento dado.

Lo que debería haber funcionado pero no
Abrir la subsección de imagen (el bit con el que queríamos recortar) con BitmapRegionDecoder , pasando en un BitmapFactory.option con inMutable = true , procesando los píxeles y luego guardándolo en el archivo.
Aunque nuestra aplicación declaró un API mínimo de 14 y un objetivo de 19, BitmapRegionDecoder estaba devolviendo un mapa de bits inmutable, haciendo caso omiso de nuestro BitMapFactory.options

Lo que no funciona

  • Abriendo una imagen mutable con BitmapFactory (que respeta nuestra opción inMutable ) y croppping it: todas las técnicas de cultivo no son imperitivas (requieren una copia de toda la imagen para existir en la memoria a la vez, aunque la basura recogida inmediatamente después de sobrescribe y reciclado )
  • La apertura de una imagen inmutable con BitmapRegionDecoder (efectivamente recortado) y la conversión a una mutable; Todas las técnicas disponibles requieren nuevamente una copia en memoria.

El gran trabajo de alrededor de 2014

  • Abrir la imagen a tamaño completo con BitmapFactory como un mapa de bits mutable, y realizar nuestras operaciones de píxeles
  • Guardar el mapa de bits en el archivo y reciclarlo desde la memoria (todavía no se recorta)
  • BitmapRegionDecoder mapa de bits guardado con BitmapRegionDecoder , abriendo sólo la región para ser recortada a (ahora no nos importa si el mapa de bits es inmutable o no)
  • Guardar este mapa de bits (que efectivamente ha sido recortado) al archivo, sobrescribiendo el mapa de bits previamente guardado (que no se recortó)

Con este método, podemos recortar y realizar el procesamiento de píxeles en un mapa de bits con sólo 1 copia siempre en la memoria (por lo que podemos evitar esos molestos errores OOM tanto como sea posible), el comercio de memoria RAM para el tiempo, ya que tenemos que realizar extra (lenta) IOs.

Sé que la pregunta está resuelta, pero ¿qué pasa con:

BitmapFactory.decodeStream(getResources().openRawResource(getResources().getIdentifier("bitmapname", "drawable", context.getPackageName())))

  • ¿Cómo limpiar un mapa de bits?
  • Android: Cómo utilizar grantUriPermission para poder crear y enviar un correo electrónico con un archivo adjunto de mapa de bits
  • Imagen de Android seleccionada de la galería La orientación siempre es 0: Exif TAG
  • Retraso en la demostración de uso de la imagen mapa de bits en android
  • Para bitmap.recycle (), o no a bitmap.recycle ()
  • Dibujar objeto / imagen sobre lienzo
  • Rotación de un mapa de bits mediante JNI y NDK
  • Guardar vista como mapa de bits, sólo obtener pantalla en negro
  • Nuevo mapa de bits modificado en la copia que utiliza el búfer
  • Dibujo punteado (...) camino en lugar de una línea (________)
  • Android Advertencia: JAR no existe o no contiene recursos
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.