Android 2.2を実行しているデバイスでアプリケーションを開発し、テストしています。私のコードでは、BitmapFactory.decodeResourceを使用して取得するビットマップを使用し、 bitmap.setPixels()
を呼び出します。Android 1.6を実行している友人のデバイスでこれをテストすると、bitmap.setPixels
への呼び出しでIllegalStateException
を取得します。オンラインのドキュメントでは、ビットマップが不変の場合、このメソッドからIllegalStateException
がスローされると書かれています。ドキュメントでは、decodeResource
が不変のビットマップを返すことについては何も述べていません。
2番目のBitmap
オブジェクトを必要とせずにアプリケーションリソースから確実に可変ビットマップを取得するために行うことができる別の呼び出しがあります(同じサイズの可変可変ビットを作成し、それをラップするCanvasに描画できますが、私が意図した2倍のメモリを使用して同じサイズの2つのビットマップを必要としますか?
不変ビットマップを可変ビットマップに変換できます。
1つのビットマップのメモリのみを使用する許容可能なソリューションを見つけました。
ソースビットマップはディスク上に未加工で保存(RandomAccessFile)され(RAMメモリなし)、ソースビットマップが解放され(メモリにビットマップがなくなりました)、その後、ファイル情報が別のビットマップにロードされます。この方法により、一度にRAMメモリに1つのビットマップのみが保存されたビットマップコピーを作成できます。
完全なソリューションと実装については、こちらをご覧ください。 Android:Immutable BitmapをMutableに変換
このソリューションに改善を加えました。これは、あらゆるタイプのビットマップ(ARGB_8888、RGB_565など)で動作し、一時ファイルを削除します。私の方法を見てください:
/**
* Converts a immutable bitmap to a mutable bitmap. This operation doesn't allocates
* more memory that there is already allocated.
*
* @param imgIn - Source image. It will be released, and should not be used more
* @return a copy of imgIn, but muttable.
*/
public static Bitmap convertToMutable(Bitmap imgIn) {
try {
//this is the file going to use temporally to save the bytes.
// This file will not be a image, it will store the raw image data.
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "temp.tmp");
//Open an RandomAccessFile
//Make sure you have added uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE"
//into AndroidManifest.xml file
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
// get the width and height of the source bitmap.
int width = imgIn.getWidth();
int height = imgIn.getHeight();
Config type = imgIn.getConfig();
//Copy the byte to the file
//Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, imgIn.getRowBytes()*height);
imgIn.copyPixelsToBuffer(map);
//recycle the source bitmap, this will be no longer used.
imgIn.recycle();
System.gc();// try to force the bytes from the imgIn to be released
//Create a new bitmap to load the bitmap again. Probably the memory will be available.
imgIn = Bitmap.createBitmap(width, height, type);
map.position(0);
//load it back from temporary
imgIn.copyPixelsFromBuffer(map);
//close the temporary file and channel , then delete that also
channel.close();
randomAccessFile.close();
// delete the temp file
file.delete();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return imgIn;
}
可変オプションtrueを使用して、ビットマップを自分自身にコピーします。このように、余分なメモリ消費も長いコード行も必要ありません。
Bitmap bitmap= BitmapFactory.decodeResource(....);
bitmap= bitmap.copy(Bitmap.Config.ARGB_8888, true);
最初にBitmapFactory.Optionsクラスをインスタンス化してBitmapFactoryのオプションを設定し、次に「inMutable」という名前のオプションフィールドをtrueに設定してから、このオプションインスタンスをdecodeResourceに渡すことができます。
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inMutable = true;
Bitmap bp = BitmapFactory.decodeResource(getResources(), R.raw.white, opt);
私はパーティーに遅れていることを知っていますが、これはこの痛みを伴う厄介なAndroid=の問題を回避し、メモリ内に1つだけのコピーがある画像をトリミングおよび変更する方法です。
状況
ファイルに保存された画像のトリミングされたバージョンのピクセルを処理します。メモリの需要が高いため、このイメージの複数のコピーを常にメモリに保存することは望ましくありません。
動作するはずだったが動作しなかったものBitmapRegionDecoder
で画像サブセクション(切り取りたいビット)を開き、BitmapFactory.option
with inMutable = true
、ピクセルを処理してからファイルに保存します。
アプリはAPIの最小値14とターゲット19を宣言しましたが、BitmapRegionDecoder
は不変のビットマップを返し、BitMapFactory.options
機能しない
BitmapFactory
(これはinMutable
オプションを尊重します)で可変イメージを開き、それをトリミングします。すべてのトリミング手法は非命令的です(一度にメモリ内に存在するためには、イメージ全体のコピーが必要です)。上書きしてリサイクルした直後にゴミが収集された場合でも)BitmapRegionDecoder
(効果的にトリミングされた)で不変の画像を開き、それを変更可能な画像に変換します。使用可能なすべての手法では、メモリ内のコピーが必要です。2014年のすばらしい回避策
BitmapFactory
を使用してフルサイズの画像を開き、ピクセル操作を実行しますBitmapRegionDecoder
で開き、トリミングする領域のみを開きます(ビットマップが不変かどうかは関係ありません)この方法を使用すると、メモリ内に1つのコピーのみを含むビットマップでトリミングおよびピクセル処理を実行できます(そのため、これらの厄介なOOMエラーを可能な限り回避できます)、RAM余分な(遅い)ファイルIOを実行する必要があります。