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;
}
- 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.
- 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 luegoBitmapFactory.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 porqueDebug.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. - 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. - 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.
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
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
Para que todos sepan que "getAvailableMemory()" puede devolver números negativos. – Zammbi