Este método leerá la información del encabezado de la imagen para determinar su tamaño, luego leerá la imagen y la escalará al tamaño deseado en su lugar sin asignar memoria para la imagen de tamaño original.
También usa BitmapFactory.Options.inPurgeable, que parece ser una opción escasamente documentada pero deseable para evitar excepciones de OoM cuando se utilizan lotes de mapas de bits. ACTUALIZACIÓN: ya no se utiliza inPurgeable, ver this note de Romain
Funciona mediante el uso de un BufferedInputStream para leer la información del encabezado de la imagen antes de leer toda la imagen en medio del InputStream.
/**
* Read the image from the stream and create a bitmap scaled to the desired
* size. Resulting bitmap will be at least as large as the
* desired minimum specified dimensions and will keep the image proportions
* correct during scaling.
*/
protected Bitmap createScaledBitmapFromStream(InputStream s, int minimumDesiredBitmapWith, int minimumDesiredBitmapHeight) {
final BufferedInputStream is = new BufferedInputStream(s, 32*1024);
try {
final Options decodeBitmapOptions = new Options();
// For further memory savings, you may want to consider using this option
// decodeBitmapOptions.inPreferredConfig = Config.RGB_565; // Uses 2-bytes instead of default 4 per pixel
if(minimumDesiredBitmapWidth >0 && minimumDesiredBitmapHeight >0) {
final Options decodeBoundsOptions = new Options();
decodeBoundsOptions.inJustDecodeBounds = true;
is.mark(32*1024); // 32k is probably overkill, but 8k is insufficient for some jpgs
BitmapFactory.decodeStream(is,null,decodeBoundsOptions);
is.reset();
final int originalWidth = decodeBoundsOptions.outWidth;
final int originalHeight = decodeBoundsOptions.outHeight;
// inSampleSize prefers multiples of 2, but we prefer to prioritize memory savings
decodeBitmapOptions.inSampleSize= Math.max(1,Math.min(originalWidth/minimumDesiredBitmapWidth, originalHeight/minimumDesiredBitmapHeight));
}
return BitmapFactory.decodeStream(is,null,decodeBitmapOptions);
} catch(IOException e) {
throw new RuntimeException(e); // this shouldn't happen
} finally {
try {
is.close();
} catch(IOException ignored) {}
}
}
También puede ser necesario para realizar una llamada explícita GC antes de llamar BitmapFactory.decodeStream, según esta respuesta: http://stackoverflow.com/questions/6718855/using-caching-to-improve-scrolling-performance -in-an-android-listview-with-images/7245318 # 7245318 – emmby
probablemente tendría sentido usar new/close en lugar de mark/reset en FileInputStream. Pero la idea es buena. – kellogs
¿Por qué obtengo 'java.io.IOException: Mark ha sido invalidado' en' is.reset(); ' – sancho21