Bitmap内存管理一些理解
bitmap内存是存储在哪里
android 3.0 到7.1 bitmap内存是放在dalvik heap中,3.0以下和 8.0 以后放在native heap中,
Bitmap复用内存
/设置成是否需要复用内存
options.inMutable=true;
options.inBitmap=multiplex;
复用是有根据不同的Android版本他的复用规则是不一样的19之前必须同等大小才能复用inSampleSize=1,19后的只要大小小于等于其大小就可以了。
Bitmap内存复用主要作用是
bitmap复用内存块,不需要在重新给这个bitmap申请一块新的内存,避免了一次内存的分配和回收,从而改善了运行效率。
对图片进行处理成适合我们展示的大小
我们从网络上下载图片的时候通常需要对下载的图片进行缩放,获取到我们需要使用到的内存图片,可根据我们需要展示的宽高来展示。
...
public static Bitmap resizeBitmapResource(Context context,int id,int wI,int hI,boolean hasAlpha,Bitmap multiplex){
Resources resources=context.getResources();
BitmapFactory.Options options=new BitmapFactory.Options();
//拿到图片的宽高即拿到系统处理的信息
options.inJustDecodeBounds=true;
//我们把原来的解码参数改了再生成bitmap
BitmapFactory.decodeResource(resources,id,options);
//取得宽高然后进行缩放
int w=options.outWidth;
int h=options.outHeight;
//设置缩放系数
options.inSampleSize=setScalingFactor(w,h,wI,hI);
//把系数恢复过来
options.inJustDecodeBounds=false;
if(!hasAlpha)//是否需要透明度
{
options.inPreferredConfig=Bitmap.Config.RGB_565;//不需要的话我们就用2个字节即16位即我们减少内存的使用。
}
//设置成是否需要复用内存
options.inMutable=true;
options.inBitmap=multiplex;
return BitmapFactory.decodeResource(resources,id,options);
}
//返回的结果是原来解码图片的要缩放的系数即我们需要显示的大小,这里找最接近2的几次方
private static int setScalingFactor(int w, int h, int wI, int hI) {
int inSampleSize=1;
if(w>wI&&h>hI){
inSampleSize=2;
while (w/inSampleSize>wI&&h/inSampleSize>hI){//这个说明还是可以再缩放
inSampleSize*=2;
}
}
return inSampleSize;
}
图片缓存
通常我们需要把这些图片缓存起来我们常用的三级缓存
一般是内存缓存和磁盘缓存
基本都是用lru算法lru其内部是LinkedHashMap
那么我来简单的说一下
lru算法采用的是最近最少使用算法
他的使用规则是当内存中只能放十张图片吧,当外面从这十张中获取的时候假如说是第五张被去出来
那么就将第五张图片断开链表然后取出来放在队头此时第四和第六就连接起来了。
如果是外面的存进来那么这个时候就把外面传进来放在队头然后把最近最少使用的那一张移出缓存
...
//参数表示能够缓存内存最大值,参数是byte所以10241024
memoryCache= new LruCache
@Override
protected int sizeOf(String key, Bitmap value) {
//19之前必须同等大小才能复用inSampleSize=1
if(Build.VERSION.SDK_INT>Build.VERSION_CODES.KITKAT){
return value.getAllocationByteCount(); //为什么要怎么写就是因为怕计算的存储数量错误,假如说这个位置每张大小是10m然后总共是100m这样子的化就只能存10个,假如这个时候有一个比较小的图片就会造成计算错误,因为第一次分配的内存之后每次都要获得这么大的内存
}
return value.getByteCount();
}
/
表示lru已经放不下了,bitmap从lru中移除对象时,会回调
*/
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if(oldValue.isMutable()){//如果是设置成能复用的内存块,
//把这些图片放到一个复用池中
reuseablePool.add(new WeakReference(oldValue,mReferenceQueue));
}
else{
//oldValue就是移出来的对象
oldValue.recycle();
}
}
};
...
...
public static Set
当弱引用被GC扫描到的数据会放到mReferenceQueue队列中这个时候外面就可以手动回收内存
private ReferenceQueue
if(mReferenceQueue==null){
//当弱引用需要被回收的时候,会进入到这个队列中去
mReferenceQueue=new ReferenceQueue
//开一个线程去获取弱引用需要被回收的数据即被GC扫描到的数据,交到native去释放
clearReferenceQueue=new Thread(new Runnable() {
@Override
public void run() {
while (!shutDown){
try {
Reference
Bitmap bitmap= bitmapReferenceQueue.get();
if(bitmap!=null&&!bitmap.isRecycled()){
bitmap.recycle();//就是不在等代gc扫描回收而是直接手动调用回收
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
clearReferenceQueue.start();
}
return mReferenceQueue;
}
...
这两段代码中注释已经说的很清楚了
当lru满的时候在增加新图片就会移除里面最近最少使用的图片,这个时候就会回调entryRemoved
在这个方法中首先判断是否我们设置成能复用的内存块
如果设置了我们就存到复用池中去,这个复用池有两个作用一个是获取内存,一个是通知要手动回收了
那么这个复用池内存是在什么时候用了其是在我们在内存缓存中找不到要用的图片的时候接着去磁盘缓存中查找或者网络中下载,
这样可以避免了一次内存的分配和回收,以及从新分配.
...
Bitmap bitmap=null;
/*
获取复用的内存
*/
public Bitmap getMultiplex(int w,int h,int inSampleSize){
if(Build.VERSION.SDK_INT
}
Bitmap multiplex=null;
Iterator
while (iterator.hasNext()){
bitmap=iterator.next().get();
if(bitmap!=null){
if(checkInBitmap(bitmap,w,h,inSampleSize)){
multiplex=bitmap;
}
iterator.remove();
}
}
return multiplex;
}
private boolean checkInBitmap(Bitmap bitmap, int w, int h, int inSampleSize) {
if(Build.VERSION.SDK_INT1){
w/=inSampleSize;
h/=inSampleSize;
}
int byteCount=w*h*getPixelsCount(bitmap.getConfig());
return byteCount
//一般我们都会设置成 Bitmap.Config.RGB_565;
private int getPixelsCount(Bitmap.Config config) {
if(config==Bitmap.Config.ARGB_8888){
return 4;
}
return 2;
}
...
这个主要是获取复用内存的