Asignación de mapa de bits, utilizando BitmapFactory.Options.inBitmap arroja IllegalArgumentException

Tengo la siguiente excepción: problema de decodificación en el mapa de bits existente , al establecer inBitmap a true;

Causado por: java.lang.IllegalArgumentException: Descifrar el problema en el mapa de bits existente
En android.graphics.BitmapFactory.decodeResource (BitmapFactory.java:460)

Lo interesante es que el mismo código falla en diferentes lugares cuando se ejecuta en:

  • API: 4.4.2, Nexus 4
  • API: 4.3.1, Samsung s3

Este es mi código que es una copia tal como se muestra en este DevBytes: Bitmap Allocation video.

 private BitmapFactory.Options options; private Bitmap reusedBitmap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final ImageView imageView = (ImageView) findViewById(R.id.image_view); // set the size to option, the images we will load by using this option options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inMutable = true; BitmapFactory.decodeResource(getResources(), R.drawable.img1, options); // we will create empty bitmap by using the option reusedBitmap = Bitmap.createBitmap(options.outWidth, options.outHeight, Bitmap.Config.ARGB_8888); // set the option to allocate memory for the bitmap options.inJustDecodeBounds = false; options.inSampleSize = 1; options.inBitmap = reusedBitmap; // #1 reusedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img1, options); imageView.setImageBitmap(reusedBitmap); imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { options.inBitmap = reusedBitmap; // #2 reusedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img2, options); imageView.setImageBitmap(reusedBitmap); } }); } 

  • Nexus 4, se bloquea en BitmapFactory.decodeResource() en // #1
  • S3, pasa el # 1 y muestra la primera imagen, pero se bloquea más tarde haciendo clic en la imagen en BitmapFactory.decodeResource() en // #2

Pocas notas:

  • Las imágenes son del mismo tamaño. He intentado jpg y png . Falla en ambos.
  • Los mapas de bits son mutables.
  • Comprobé usando este método canUseForInBitmap , como se describe aquí .

Pregunta:

¿Cómo usar esta propiedad inBitmap correctamente?

Si usted encontró tal problema o usted ve que hice algo estúpido, por favor comente / conteste. Cualquier ayuda, será apreciada. Si conoce alguna solución, será genial.

– Editar (la pregunta sigue abierta) –

Lo siento por no explicar la razón de por qué estoy tratando de reutilizar bitmaps de tal manera.
La razón de esto es GC que bloquea cada vez que él decide liberar memoria.
inBitmap función inBitmap debería ayudarnos a reutilizar el mapa de bits sin asignar nueva memoria, lo que hará que GC limpie la memoria ya asignada.

Por ejemplo, si utilizo este enfoque común:

 Log.i("my_tag", "image 1"); imageView.setImageResource(R.drawable.img1); Log.i("my_tag", "image 2"); imageView.setImageResource(R.drawable.img2); Log.i("my_tag", "image 3"); imageView.setImageResource(R.drawable.img3); 

Entonces este será el trabajo de GC :

 I/my_tag ( 5886): image 1 D/dalvikvm( 5886): GC_FOR_ALLOC freed 91K, 2% free 9113K/9240K, paused 15ms, total 15ms I/dalvikvm-heap( 5886): Grow heap (frag case) to 19.914MB for 11520016-byte allocation D/dalvikvm( 5886): GC_FOR_ALLOC freed <1K, 1% free 20362K/20492K, paused 13ms, total 13ms I/my_tag ( 5886): image 2 D/dalvikvm( 5886): GC_FOR_ALLOC freed 11252K, 2% free 9111K/9236K, paused 15ms, total 15ms I/dalvikvm-heap( 5886): Grow heap (frag case) to 19.912MB for 11520016-byte allocation D/dalvikvm( 5886): GC_FOR_ALLOC freed <1K, 1% free 20361K/20488K, paused 35ms, total 35ms I/my_tag ( 5886): image 3 D/dalvikvm( 5886): GC_FOR_ALLOC freed 11250K, 2% free 9111K/9236K, paused 15ms, total 15ms I/dalvikvm-heap( 5886): Grow heap (frag case) to 19.913MB for 11520016-byte allocation D/dalvikvm( 5886): GC_FOR_ALLOC freed <1K, 1% free 20361K/20488K, paused 32ms, total 32ms 

Esto es más de 100ms de hilo principal bloqueado!

Lo mismo sucederá si decodeResource() sin la opción inBitmap . Así que la pregunta sigue abierta, ¿cómo usar esta propiedad?

He probado su código con el emulador Nexus 4.
Tenía el archivo predeterminado ic_launcher.png que copiar y pegar dos veces en drawable-mdpi (como suelo hacer). He cambiado el nombre de los dos nuevos archivos para que coincidan con los nombres en su código (por lo que tengo menos cambios que hacer allí).
Cuando ejecuto la aplicación observé lo mismo que tú.
Después de algunos intentos diferentes decidí copiar los nuevos pngs a otras carpetas dibujables – por lo que estaban presentes en:

  • Drawable-hdpi
  • Drawable-mdpi
  • Dibujable xhdpi
  • Drawable-xxhdpi

Ejecuto la aplicación y está funcionando!

No estoy realmente seguro de por qué realmente funciona, pero obviamente tiene que ser algo con la resolución de pantalla correcta / densidad.

Es un poco extraño que crear un nuevo mapa de bits y luego tratar de reutilizarlo. ¿Por qué no dejar que decodeResource cree un nuevo mapa de bits en primer lugar? Sospecho que su problema en # 2 sin embargo es que una vez que usted ha fijado el ImageView para utilizar el mapa de bits, no puede reutilizarlo más (porque está ya en uso). La IllegalArgumentException se menciona en los documentos :

Si la operación de descodificación no puede utilizar este mapa de bits, el método de descodificación volverá nulo y lanzará una excepción IllegalArgumentException.

En cuanto a una solución, puede intentar mantener dos mapas de bits alrededor y cambiar alrededor de que el ImageView está apuntando a, por ejemplo:

  1. Decodificar a mapa de bits 1, punto ImageView a mapa de bits 1
  2. Decodificar a mapa de bits 2, punto ImageView a mapa de bits 2
  3. Repita desde el paso 1

Deberá establecer options.inMutable en true antes de # 1 & # 2 en su código. Esto puede sonar un poco añadir, pero estas son pequeñas minas que sobraron que usted ha pisado.

Espero que esto ayude

¿Por qué está descifrando bitmaps? Solo haz:

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final ImageView imageView = (ImageView) findViewById(R.id.image_view); imageView.setImageResource(R.drawable.img1); imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { imageView.setImageResource(R.drawable.img2); } }); } 

¿Estás seguro de que ambas imágenes tienen la misma dimensión? Según la documentación

Si se establece [inBitmap], los métodos de decodificación que toman el objeto Opciones intentarán reutilizar este mapa de bits al cargar contenido. Si la operación de descodificación no puede utilizar este mapa de bits, el método de descodificación volverá nulo y lanzará una excepción IllegalArgumentException. La implementación actual requiere que el mapa de bits reutilizado sea mutable y el mapa de bits reutilizado resultante seguirá siendo mutable incluso al descodificar un recurso que normalmente resultaría en un mapa de bits inmutable.

Debería utilizar siempre el mapa de bits devuelto del método de descodificación y no asumir que la reutilización del mapa de bits funcionó, debido a las restricciones descritas anteriormente ya las situaciones de fallo que pueden producirse. Comprobar si el valor devuelto coincide con el valor de inBitmap establecido en la estructura Opciones indicará si el mapa de bits se ha reutilizado, pero en todos los casos debe utilizar el mapa de bits devuelto por la función de decodificación para asegurarse de que está utilizando el mapa de bits que se utilizó como El destino de la decodificación.

Uso con BitmapFactory

A partir de KITKAT, cualquier mapa de bits mutable puede ser reutilizado por BitmapFactory para decodificar cualquier otro mapa de bits siempre y cuando el recuento de bytes resultante del mapa de bits decodificado sea menor o igual al recuento de bytes asignado del mapa de bits reutilizado. Esto puede ser debido a que el tamaño intrínseco es menor, o su tamaño después de la escala (para la densidad / tamaño de la muestra) es menor.

Antes de KITKAT se aplican restricciones adicionales: La imagen que se está decodificando (ya sea como recurso o como flujo) debe estar en formato jpeg o png. Sólo se admite bitmaps de igual tamaño, con inSampleSize establecido en 1. Además, la configuración del mapa de bits reutilizado anulará la configuración de inPreferredConfig, si se establece.


EDITAR:

Si tiene un mapa de bits de grandes recursos, sólo tiene que cargarlo en asynctask … Más información AQUÍ, pero se puede hacer mucho más simple.

No estoy seguro sobre el problema que usted encontró con Samsung S3 en # 2. Pero el problema que usted encontró con Nexus 4 puede ser porque usted puso dos imagen en la carpeta plegable del dpi incorrecto. Así que cuando intenta decodificar el mapa de bits, no puede encontrar los recursos.

Mi teléfono tiene densidad de pantalla es hdpi, al principio intento poner dos imágenes en drawable-mdpi, y me enfrento al problema con # 1. Así que lo cambié a drawable-hdpi, y funcionó.

Sólo tienes que poner largeHeap = "true" en la etiqueta de la aplicación (AndroidManifest.xml)

  • Posibilidad de pérdida de memoria no controlada
  • Colección de cuerdas y basura
  • Cómo escuchar eventos de GC en Android
  • Un montón de recolección de basura en un listview
  • Cómo simular la muerte de la aplicación de Android GC
  • Android: ¿el GC no respeta SoftReferences?
  • Referencia débil para la llamada de red mala idea?
  • ¿Cómo encontrar el origen de un GC_FOR_MALLOC?
  • OnReceive Asynchronous Operations y recolección de basura
  • ¿Está creando lo siguiente para loop bad para Garbage Collection?
  • La actividad principal no es la basura recogida después de la destrucción porque es referenciada por InputMethodManager indirectamente
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.