Ta metoda odczyta informacje nagłówka z obrazu w celu ustalenia jego rozmiaru, a następnie odczyta obraz i przeskaluje go do wymaganego rozmiaru w miejscu bez przydzielania pamięci dla pełnego obrazu o oryginalnym rozmiarze.
Używa również BitmapFactory.Options.inPurgeable, która wydaje się być słabo udokumentowaną, ale pożądaną opcją, aby zapobiec wyjątkom OoM podczas korzystania z dużej ilości bitmap. UPDATE: nie używa inPurgeable patrz this note od Romain
Działa przy użyciu BufferedInputStream przeczytać informacje nagłówka dla obrazu przed odczytaniem całego obrazu w poprzez 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) {}
}
}
Być może trzeba dokonać wyraźnego połączenia GC przed wywołaniem BitmapFactory.decodeStream, jak na to odpowiedź: http://stackoverflow.com/questions/6718855/using-caching-to-improve-scrolling-performance -in-an-android-listview-with-images/7245318 # 7245318 – emmby
Prawdopodobnie miałoby sens użycie nowego/zamknięcia zamiast znaku/resetowania w FileInputStream. Ale pomysł jest dobry. – kellogs
Dlaczego otrzymuję 'java.io.IOException: Mark został unieważniony' na' is.reset(); ' – sancho21