Java BitmapFactory.decodeResource在Android 2.2中返回可变位图,在Android 1.6中返回不可变位图
我正在开发一个应用程序,并在运行安卓2.2的设备上进行测试。在我的代码中,我使用使用BitmapFactory.decodeResource检索的位图,我可以通过调用位图上的Java BitmapFactory.decodeResource在Android 2.2中返回可变位图,在Android 1.6中返回不可变位图,java,android,bitmap,Java,Android,Bitmap,我正在开发一个应用程序,并在运行安卓2.2的设备上进行测试。在我的代码中,我使用使用BitmapFactory.decodeResource检索的位图,我可以通过调用位图上的Bitmap.setPixels()进行更改。当我在一个运行Android 1.6的朋友的设备上测试时,我在调用bitmap.setPixels时得到一个IllegalStateException。在线文档说明,当位图不可变时,此方法会引发IllegalStateException。文档中没有提到任何关于decodeReso
Bitmap.setPixels()
进行更改。当我在一个运行Android 1.6的朋友的设备上测试时,我在调用bitmap.setPixels
时得到一个IllegalStateException
。在线文档说明,当位图不可变时,此方法会引发IllegalStateException
。文档中没有提到任何关于decodeResource
返回不可变位图的内容,但很明显,情况必须如此
我是否可以调用另一个调用来可靠地从应用程序资源获取可变位图,而不需要第二个
位图
对象(我可以创建一个大小相同的可变位图并将其绘制到画布中,但这需要两个大小相同的位图,占用的内存是我预期的两倍)?使用可变选项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);
您可以将不可变位图转换为可变位图 我找到了一个可以接受的解决方案,它只使用一个位图的内存 源位图原始保存在磁盘上(无ram内存),然后释放源位图(现在,内存中没有位图),然后将文件信息加载到另一个位图。这种方法可以使位图副本每次只在ram内存中存储一个位图 请参见此处的完整解决方案和实施: 我为这个解决方案添加了一个改进,现在可以使用任何类型的位图(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;
}
这是我创建的一个解决方案,它使用内部存储,不需要任何新的许可,这是基于“Derzu”的想法,并且从蜂巢开始,它是内置的:
/**decodes a bitmap from a resource id. returns a mutable bitmap no matter what is the API level.<br/>
might use the internal storage in some cases, creating temporary file that will be deleted as soon as it isn't finished*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static Bitmap decodeMutableBitmapFromResourceId(final Context context, final int bitmapResId) {
final Options bitmapOptions = new Options();
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB)
bitmapOptions.inMutable = true;
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), bitmapResId, bitmapOptions);
if (!bitmap.isMutable())
bitmap = convertToMutable(context, bitmap);
return bitmap;
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static Bitmap convertToMutable(final Context context, final Bitmap imgIn) {
final int width = imgIn.getWidth(), height = imgIn.getHeight();
final Config type = imgIn.getConfig();
File outputFile = null;
final File outputDir = context.getCacheDir();
try {
outputFile = File.createTempFile(Long.toString(System.currentTimeMillis()), null, outputDir);
outputFile.deleteOnExit();
final RandomAccessFile randomAccessFile = new RandomAccessFile(outputFile, "rw");
final FileChannel channel = randomAccessFile.getChannel();
final MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, imgIn.getRowBytes() * height);
imgIn.copyPixelsToBuffer(map);
imgIn.recycle();
final Bitmap result = Bitmap.createBitmap(width, height, type);
map.position(0);
result.copyPixelsFromBuffer(map);
channel.close();
randomAccessFile.close();
outputFile.delete();
return result;
} catch (final Exception e) {
} finally {
if (outputFile != null)
outputFile.delete();
}
return null;
}
但是,我不确定API级别的最低要求是什么。它在API 8及以上版本上运行得非常好。我知道问题已经解决了,但是关于:
BitmapFactory.decodeStream(getResources().openRawResource(getResources().getIdentifier(“bitmapname”,“drawable”,context.getPackageName()))))
我知道我来晚了,但这就是我们如何避免这个令人痛苦的恼人的Android问题,并在内存中仅使用一个副本剪切和修改图像的方法
形势我们要处理保存到文件中的图像的裁剪版本的像素。由于内存需求很高,我们永远不希望在任何给定的时间内存中有此图像的多个副本 什么应该有效,但没有
使用
BitMapRegionCoder
打开图像子部分(我们要裁剪的位),传入BitmapFactory.option
和inMutable=true
,处理像素,然后保存到文件中。尽管我们的应用程序声明API最小值为14,目标值为19,
BitMapRegionCoder
返回一个不可变位图,实际上忽略了我们的BitMapFactory.options
什么不起作用
- 使用
打开可变图像(这尊重我们的BitmapFactory
选项)并对其进行裁剪:所有裁剪技术都是非直接的(要求一次将整个图像的副本存在于内存中,即使在覆盖和回收之后立即收集垃圾)inMutable
- 使用BitMapRegionCoder(有效裁剪)打开不可变图像,并将其转换为可变图像;所有可用的技术都需要在内存中复制
- 使用
作为可变位图打开全尺寸图像,并执行像素操作BitmapFactory
- 将位图保存到文件并从内存中回收(它仍然未裁剪)
- 使用
打开保存的位图,只打开要裁剪的区域(现在我们不关心位图是否不可变)BitMapRegionCoder
- 将此位图(已有效裁剪)保存到文件,覆盖以前保存的位图(未裁剪)
使用这种方法,我们可以在内存中只有一个拷贝的位图上裁剪和执行像素处理(因此我们可以尽可能避免那些讨厌的OOM错误),以RAM换取时间,因为我们必须执行额外(缓慢)的文件IOs 之所以发生这种情况,是因为您希望通过调用
setHeight()
或setWidth()
调整任何位图或可绘制(Png、Svg、矢量等)的大小
用法示例:
Bitmap facebookIcon = editMyBitmap(R.drawable.facebookImage);
// now use it anywhere
imageView.setBitmapImage(facebookIcon);
canvas.drawBitmap(facebookIcon, 0, 0, null);
是的,但是在大图像上的内存消耗太多了。我想我需要多步骤来完成这项工作-使用BitmapFactory.Options.inJustDecodeBounds只获取维度,创建一个可变的空位图,并从原始数据的输入流中填充它。让我明白的是,文档详细说明了那些返回不可变位图的方法,而这个方法没有指定(暗示默认为可变)。它在我的2.2手机上是这样工作的,但在我朋友的1.6手机上不是这样的。你有这样的例子吗。我知道这是一个非常古老的职位。但仍希望得到响应。我在以这种方式复制位图时遇到问题-新位图的宽度和高度为-1,因此无法渲染。位图未渲染。为此,位图为+1,但仅在api级别11及更高版本中可用。我的问题是如何向后兼容1.6(api 4)
public Bitmap editMyBitmap(int drawableId, int newHeight, int newWidth) {
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), drawableId);
myBitmap = Bitmap.createScaledBitmap(myBitmap, newWidth, newHeight, false);
return myBitmap;
}
Bitmap facebookIcon = editMyBitmap(R.drawable.facebookImage);
// now use it anywhere
imageView.setBitmapImage(facebookIcon);
canvas.drawBitmap(facebookIcon, 0, 0, null);