Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/207.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java BitmapFactory.decodeResource在Android 2.2中返回可变位图,在Android 1.6中返回不可变位图_Java_Android_Bitmap - Fatal编程技术网

Java BitmapFactory.decodeResource在Android 2.2中返回可变位图,在Android 1.6中返回不可变位图

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

我正在开发一个应用程序,并在运行安卓2.2的设备上进行测试。在我的代码中,我使用使用BitmapFactory.decodeResource检索的位图,我可以通过调用位图上的
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(有效裁剪)打开不可变图像,并将其转换为可变图像;所有可用的技术都需要在内存中复制
2014年的伟大工作

  • 使用
    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);