最近做的一个项目,需要获取图片中每个像素点的信息,然后再进行一定顺序的重新排列。一开始,将需要处理的图片放到了Drawable中,通过getPixels()方法获取了整张图片的信息,然后处理完之后直接通过setPixels()就得到了新的图片。很好,然后抽风将图片放到了sdcard中,用同样的方法处理后报异常:java.lang.IllegalStateException……
最后发现Bitmap中有一个isMutable的字段,我的理解就是是否可变,也不知道对不对。而通过BitmapFactory.decodeResource(Resources res, int id)获取到的bitmap是mutable的,而BitmapFactory.decodeFile(String path)获取到的是immutable的,因此导致了前面所说的异常。(查看源码时看到走了好多方法,并没有仔细研究为什么两者生成的bitmap一个是mutable,一个是immutable的,如果哪位大虾能说清楚些希望指点一下…)
通过查找资料,stackoverflow有位大神给出了如下的方法,将immutable的bitmap转为了mutable的:
/** * 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; }
用这个方法转一下,解决了异常。后来查找每个生成bitmap的方法,发现只有Bitmap createBitmap(int width, int height, Config config) 和Bitmap createBitmap(int width, int height,Config config, boolean hasAlpha)是Returns a mutable bitmap。(前者就是hasAlpha为true的后者,你懂得),故在进行setPixels前重新通过createBitmap(width, height, Config.ARGB_8888)创建了一个bitmap(他是mutable的),然后再setPixels(),也解决了异常,不过感觉后者的效率能够高些,看第一个方法还创建了个临时文件,感觉可能会很卡,但测试是两个都挺卡……准备做个像ViewPager一样的缓存,这样可能不会那么卡吧……
参考资料: