首先Bitmap在Android虚拟机中的内存分配,在Google的网站上给出了下面的一段话
大致的意思也就是说,在Android3.0之前,Bitmap的内存分配分为两部分,一部分是分配在Dalvik的VM堆中,而像素数据的内存是分配在Native堆中,而到了Android3.0之后,Bitmap的内存则已经全部分配在VM堆上,这两种分配方式的区别在于,Native堆的内存不受Dalvik虚拟机的管理,我们想要释放Bitmap的内存,必须手动调用Recycle方法,而到了Android 3.0之后的平台,我们就可以将Bitmap的内存完全放心的交给虚拟机管理了,我们只需要保证Bitmap对象遵守虚拟机的GC Root Tracing的回收规则即可。OK,基础知识科普到此。接下来分几个要点来谈谈如何优化Bitmap内存问题。
针对3.0版本的优化方案,请看以下代码,
private int mCacheRefCount = 0;//缓存引用计数器
private int mDisplayRefCount = 0;//显示引用计数器
...
// 当前Bitmap是否被显示在UI界面上
public void setIsDisplayed(boolean isDisplayed) {
synchronized (this) {
if (isDisplayed) {
mDisplayRefCount++;
mHasBeenDisplayed = true;
} else {
mDisplayRefCount--;
}
}
checkState();
}
//标记是否被缓存
public void setIsCached(boolean isCached) {
synchronized (this) {
if (isCached) {
mCacheRefCount++;
} else {
mCacheRefCount--;
}
}
checkState();
}
//用于检测Bitmap是否已经被回收
private synchronized void checkState() {
if (mCacheRefCount reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
long totalPixels = width / inSampleSize * height / inSampleSize ;
final long totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels > totalReqPixelsCap) {
inSampleSize *= 2;
totalPixels /= 2;
}
}
return inSampleSize;
5.采用decodeFileDescriptor来编码图片,比直接使用decodeFile更省内存
查看BitmapFactory的源码,对比一下两者的实现,可以发现decodeFile()最终是以流的方式生成bitmap
decodeFile源码:
- public static Bitmap decodeFile(String pathName, Options opts) {
- Bitmap bm = null;
- InputStream stream = null;
- try {
- stream = new FileInputStream(pathName);
- bm = decodeStream(stream, null, opts);
- } catch (Exception e) {
- /* do nothing.
- If the exception happened on open, bm will be null.
- */
- } finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException e) {
- // do nothing here
- }
- }
- }
- return bm;
- }
decodeFileDescriptor的源码,可以找到native本地方法decodeFileDescriptor,通过底层生成bitmap
decodeFileDescriptor源码:
- public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
- if (nativeIsSeekable(fd)) {
- Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
- if (bm == null && opts != null && opts.inBitmap != null) {
- throw new IllegalArgumentException("Problem decoding into existing bitmap");
- }
- return finishDecode(bm, outPadding, opts);
- } else {
- FileInputStream fis = new FileInputStream(fd);
- try {
- return decodeStream(fis, outPadding, opts);
- } finally {
- try {
- fis.close();
- } catch (Throwable t) {/* ignore */}
- }
- }
- }
- private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,Rect padding, Options opts);