2011-06-01 14 views
12

Para facilitar su lectura, publiqué los ejemplos de código a los que se refieren mis soluciones primero, y luego indiqué las explicaciones de mis soluciones en una lista numérica.Android: Pregunta sobre mapas de bits, uso de memoria y escalado

He estado luchando con esto desde hace un tiempo. He leído mucho, hice preguntas aquí y experimenté; pero no han encontrado una solución decente. Necesito leer imágenes de diferentes tamaños de flujos de entrada y mostrarlas con la calidad más alta que permitan mis limitaciones de memoria. A continuación están las opciones que he considerado, ninguna de las cuales me parece genial. Cualquier ayuda o entrada sería muy apreciada.

public class NativeTest extends Activity 
{ 
    @Override 
    public void onCreate(Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 
     double nativeUsage = Debug.getNativeHeapAllocatedSize(); 
     Log.i("memory", nativeUsage+""); 
    } 
} 


double getAvailableMemory() 
{ 
    //current heap size 
    double heapSize = Runtime.getRuntime().totalMemory(); 
    //amount available in heap 
    double heapRemaining = Runtime.getRuntime().freeMemory(); 
    double nativeUsage = Debug.getNativeHeapAllocatedSize(); 
    double memoryAvailable = Runtime.getRuntime().maxMemory() - (heapSize - heapRemaining) - nativeUsage; 
    return memoryAvailable; 
} 

Bitmap createImageTrialAndError(InputStream stream) 
{ 
    Bitmap image = null; 
    int dowsample = 1; 
    while(image == null) 
    { 
     try 
     {  
     Options opts = new Options(); 
     opts.inSampleSize = downsample; 
     image = BitmapFactory.decodeStream(imgStream, null, opts); 
     } 
     catch (OutOfMemoryError ome) 
     { 
      downsample = downsample * 2; 
      Log.i("out of mem", "try using: " + downsample); 
     } 
    } 

    return image; 
} 
  1. La solución ideal sería si Bitmap tenía un método Bitmap.drawBitmap (flujo de entrada ...). Esto me permitiría extraer de la ruta de entrada sin tener que asignar memoria para el mapa de bits. Por desgracia, esta no es una opción.
  2. Escale el mapa de bits según la memoria disponible. Esto implica recuperar el ancho y la altura del mapa de bits, calcular el número de bytes que Bitmap requiere como width * height * 4, calcular la memoria disponible y luego BitmapFactory.Options.inSampleSize de modo que el mapa de bits utilizará menos memoria que la disponible. Sin embargo, esta opción falla porque no he podido encontrar una forma remotamente confiable de calcular la memoria disponible. El método getAvailableMemory() a continuación parece que debería funcionar: calcula la memoria disponible como la memoria máxima, la memoria utilizada en el montón de Java, la memoria utilizada en el montón nativo.
    Desafortunadamente, esta fórmula brinda resultados poco confiables. Principalmente porque Debug.getNativeHeapAllocatedSize() no parece ser una representación exacta del uso de la memoria de mapa de bits. Un ejemplo obvio de su inexactitud es la actividad NativeTest, a continuación. En mi tableta Samsung Galaxy, la instrucción de registro emitida es: 3759416.0. 3.75 mb de asignación nativa para una actividad vacía, claramente no es una manera confiable de determinar la escala de mapa de bits.
  3. Comience con un factor de escala de uno e intente inicializar el mapa de bits; si la inicialización falla debido a la memoria, duplique el factor de escala e intente de nuevo; y repita este proceso hasta que tenga éxito. Esto se ilustra por createBitmapTrialAndError(). Esto es realmente sorprendentemente efectivo y no terriblemente lento. Sin embargo, es muy indeseable porque utilizo SoftReferences en otra parte de mi aplicación, y la falta de memoria obliga a la recopilación de estas SoftReferences, lo que tiene un impacto significativo en el rendimiento. Sería mucho más conveniente conocer el factor de escala adecuado inicialmente, lo que evitaría la recopilación innecesaria de estas referencias suaves.
  4. Mi solución final parece un poco cuestionable. Básicamente combina 2 & 3, pero no usa Debug.getNativeAllocatedSize(). En cambio, rastrearé mi propia asignación de memoria Bitmap, rastreando en cualquier lugar que asigne Bitmaps y contabilizando su uso de memoria, y luego restando el uso de memoria de cualquier Bitmap que recicle. Utilizo este valor en lugar del nativeUsage en getAvailableMemory(), para calcular el factor de escala adecuado para los mapas de bits. Y en caso de que ocurra una excepción de falta de memoria al usar este método, utilizo la solución 3 como una forma alternativa de calcular una escala aceptable. El problema obvio con esto es la gran cantidad de problemas al tratar de rastrear mi propio uso de la memoria nativa, pero, para mí, parece ser la mejor solución.
+0

Opción 4 parece ser lo que le pegan con. He trabajado un poco para tratar de cargar algunos cientos de mapas de bits al mismo tiempo y escalarlos dinámicamente en función de lo grandes que realmente estarían en la pantalla. En su caso, usar su mejor memoria de adivinar + es una buena manera de hacerlo. Nota: la opción 1 aún requerirá que asigne la memoria para el objeto así que no se preocupe porque esa función no exista. – Haphazard

+1

Su mapa de bits no puede ser más grande que el tamaño de pantalla, por lo que es un buen lugar para comenzar con SampleSize, puede usar decodeInBounds para obtener las dimensiones completas del mapa de bits sin leer todo en la memoria y luego puede leerlo en la muestra. También en 3.0+ los mapas de bits se almacenan en el montón de Java y no en la memoria nativa. – smith324

+3

Para que todos sepan que "getAvailableMemory()" puede devolver números negativos. – Zammbi

Respuesta

0

El problema del montón de Android es que usted no sabe realmente cuánto montón puede usar, ya que cualquier servicio en segundo plano podría en cualquier momento arruinar todo para usted, si excede las limitaciones de memoria.

¿Por qué no mantienes un mapa de bits del tamaño del lienzo en el que siempre estás dibujando y una pila de mapas de bits reducidos? A continuación, puede representar todas las imágenes en una solución nativa en su lienzo, y siempre dibujar sus mapas de bits no muestreados para cualquier cambio que se produzca.Una vez que el cambio ha terminado, o está claro qué imagen es más importante, vuelva a dibujar esa en resolución nativa en el lienzo (accediendo al disco de nuevo).

0

Una manera simple y directa es usar la propiedad "inJustDecodeBounds" de Opciones. Establezca esta propiedad en true para el objeto de opciones que ha creado, luego continúe para decodificar la secuencia.

El mapa de bits devuelto será nulo, lo que significa que no tiene memoria asignada, pero puede leer las dimensiones del mapa de bits y así determinar su tamaño y ajustar la proporción en Tamaño de muestra.

Restablecimiento posterior en JustDecodeBounds a false, ahora conoce el factor de reducción de escala, por lo que ahora se puede generar el mapa de bits del tamaño requerido.

Espero que esto ayude.

Cuestiones relacionadas