前言
A:透明度
R:红色
G:绿
B:蓝
Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位;
Bitmap.Config ARGB_8888:每个像素占八位,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位;
Bitmap.Config RGB_565:每个像素占四位,即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位;
Bitmap.Config ALPHA_8:每个像素占四位,只有透明度,没有颜色。
思考优化点
- Android中一般都是默认使用到ARGB_8888,由此可见,显示出图片会相比其他几种多耗费多少内存;
- ui给出来的切图,没有正确的存放到对应资源文件夹下会不会在加载的时候造成影响?
- 怎么样取做优化?
案例
相同的一张图,放入到不同的资源文件夹下面,所被加载时所占用的内存大小和获取到的图片宽高不一致;
获取Bitmap占用的字节数:getByteCount();
注意:wyz_p这张图片的宽、高,和它现在存放的文件夹(mipmap-hdpi),
图片852X1048 内存大小:3571584 ==3MB
和上面是相同的一张图片,只是放入到了对应的文件夹下面之后;
注意:wyz_p这张图片的宽、高,和它现在存放的文件夹(mipmap-xxhdpi),
图片426X524 内存大小:892896 == 1MB都没到
根据上面的案例,能明白图片是需要放到对应的文件夹下面
图片所占内存空间计算:内存大小计算:宽 X 高 X一个像素点所占的内存空间;
因为图片是ARGB_8888格式,所以一个像素点占4个字节;
优化一
思考点:
我们是不是可以考虑换成其他格式?
比如说换成RGB_565?不需要透明度的图片,我们把它的透明度通道去掉,我们来实践一下;
public static Bitmap resizeBitmap(Context context, int id, boolean hasAlpha) {
Resources resources = context.getResources();
BitmapFactory.Options options = new BitmapFactory.Options();
//需要拿得到系统处理的信息 比如解码出宽高,....
options.inJustDecodeBounds = true;
//我们把原来的解码参数改了再去生成bitmap
BitmapFactory.decodeResource(resources, id, options);
if(!hasAlpha){
options.inPreferredConfig=Bitmap.Config.RGB_565;
}
options.inJustDecodeBounds=false;
return BitmapFactory.decodeResource(resources,id,options);
}
//调用
Bitmap bitmap2=ImageResize.resizeBitmap(getApplicationContext(),R.mipmap.wyz_p,false);
我们再来打印下:
看打印kai: RGB_565图片426X524 内存大小:446448;
宽高是一样的,内存明显减小了一半;
因为RGB_565 是 一个像素占用2个字节,所以是426X524X2 = 446448;
通过上面的格式通道调整,我们知道不需要透明通道图片,是可以去掉Alpha通道的,是可以用RGB_565的
优化二
是否还可以继续优化?
思考点
图片的宽高,是不是我们理想的宽高?是不是可以进行缩放操作?
我们来实践下
这里我们来设置下缩放系数,根据传入进来所需的宽高进行缩放到最接近的宽高
public static Bitmap resizeBitmap(Context context, int id, int maxW, int maxH, boolean hasAlpha) {
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 = calcuteInSampleSize(w, h, maxW, maxH);
if(!hasAlpha){
options.inPreferredConfig=Bitmap.Config.RGB_565;
}
options.inJustDecodeBounds=false;
return BitmapFactory.decodeResource(resources,id,options);
}
//返回结果是原来解码的图片的大小 是我们需要的大小的 最接近2的几次方倍
private static int calcuteInSampleSize(int w, int h, int maxW, int maxH) {
int inSampleSize = 1;
if (w > maxW && h > maxH) {
inSampleSize = 2;
while (w / inSampleSize > maxW && h / inSampleSize > maxH){
inSampleSize*=2;
}
}
inSampleSize/=2;
return inSampleSize;
}
//调用
Bitmap bitmap2=ImageResize.resizeBitmap(getApplicationContext(),R.mipmap.wyz_p,100,100,false);
我们再来看看打印
kai: 图片107X131 内存大小:28034
我们传入了宽高都是100,通过计算,得出最接近的图片尺寸107X131;
所占内存大小又小了不少
优化三(内存复用)
我想大家都知道,Bitmap是很耗内存的,如果不使用第三方的图片加载框架的时候,我们在使用列表去加载图片的时候,图片资源特别多的时候,Bitmap不断的开辟内存空间,就会造成内存溢出的情况。
如果对Bitmap熟悉的人,我想也应该知道bitmap的内存块复用:options.inBitmap,只要当前需要用到内存块比这一块小,宽高小一点就能够进行复用;
如果要复用,就一定要设置异变 options.inMutable=true;
复用内存块:options.inBitmap=bitmap;
/**
* 缩放bitmap 可复用Bitmap内存块
* @param context
* @param id
* @param maxW
* @param maxH
* @return
*/
public static Bitmap resizeBitmap(Context context,int id,int maxW,int maxH,boolean hasAlpha,Bitmap reusable){
Resources resources = context.getResources();
BitmapFactory.Options options = new BitmapFactory.Options();
// 只解码出 outxxx参数 比如 宽、高
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources,id,options);
//根据宽、高进行缩放
int w = options.outWidth;
int h = options.outHeight;
//设置缩放系数
options.inSampleSize = calcuteInSampleSize(w,h,maxW,maxH);
if (!hasAlpha){
options.inPreferredConfig = Bitmap.Config.RGB_565;
}
options.inJustDecodeBounds = false;
//设置成能复用
options.inMutable=true;
options.inBitmap=reusable;
return BitmapFactory.decodeResource(resources,id,options);
}
/**
* 计算缩放系数
* @param w
* @param h
* @param maxW
* @param maxH
* @return 缩放的系数
*/
private static int calcuteInSampleSize(int w,int h,int maxW,int maxH) {
int inSampleSize = 1;
if (w > maxW && h > maxH){
inSampleSize = 2;
//循环 使宽、高小于 最大的宽、高
while (w /inSampleSize > maxW && h / inSampleSize > maxH){
inSampleSize *= 2;
}
}
return inSampleSize;
}
优化四(内存缓存+磁盘缓存+内存复用)
还有如果看过Glide源码的同学,应该会知道它里面有个磁盘缓存,我们可以根据这个磁盘缓存来修改成我们的磁盘缓存。
可以减少网络加载图片的请求时间
内存缓存和磁盘缓存,我们可以用Lru策略(最近最少使用策略)来进行缓存;
Lru策略就是:一个个的节点通过双向链表来保存,如果缓存的个数到了极限,这个时候在进来一个新节点,就会从队尾删除一个节点,将这个新增的节点放入到队伍的最前面;如果是队列中的某一个节点被提取存放进来的时候,会将这个节点取出来,放到队伍的最前面;
双向链表的头指针和尾指针是连接起来的;
github-DiskLruCache
将这个库下载下来,我们来使用,我们先来看看这个库里面的部分源码;
LinkedHashMap是维护了一个Entry的双向链表;
内部还有个线程池
创建对象的时候传入maxSize ,也就是给这个
public final class DiskLruCache implements Closeable {
......
......
//双向链表
private final LinkedHashMap<String, Entry> lruEntries =
new LinkedHashMap<String, Entry>(0, 0.75f, true);
//线程池
final ThreadPoolExecutor executorService =
new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
......
......
private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
this.directory = directory;
this.appVersion = appVersion;
this.journalFile = new File(directory, JOURNAL_FILE);
this.journalFileTmp = new File(directory, JOURNAL_FILE_TEMP);
this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP);
this.valueCount = valueCount;
this.maxSize = maxSize;
}
/**
* 通过传入文件目录,
* Opens the cache in {@code directory}, creating a cache if none exists
* there.
*
* @param directory a writable directory
* @param valueCount the number of values per cache entry. Must be positive.
* @param maxSize the maximum number of bytes this cache should use to store
* @throws IOException if reading or writing the cache directory fails
*/
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
throws IOException {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
if (valueCount <= 0) {
throw new IllegalArgumentException("valueCount <= 0");
}
// If a bkp file exists, use it instead.
File backupFile = new File(directory, JOURNAL_FILE_BACKUP);
if (backupFile.exists()) {
File journalFile = new File(directory, JOURNAL_FILE);
// 如果日志文件也存在,删除备份文件
if (journalFile.exists()) {
backupFile.delete();
} else {
renameTo(backupFile, journalFile, false);
}
}
// Prefer to pick up where we left off.
DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
if (cache.journalFile.exists()) {
try {
cache.readJournal();
cache.processJournal();
cache.journalWriter = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(cache.journalFile, true), Util.US_ASCII));
return cache;
} catch (IOException journalIsCorrupt) {
System.out
.println("DiskLruCache "
+ directory
+ " is corrupt: "
+ journalIsCorrupt.getMessage()
+ ", removing");
cache.delete();
}
}
// Create a new empty cache.
directory.mkdirs();
cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
cache.rebuildJournal();
return cache;
}
......
......
}
下面是我们自己的单例类
内存缓存用到的是LruCache;
磁盘缓存用到的是DiskLruCache;
定义了一个Set的复用池,里面存放 弱引用Bitmap,但是这个Bitmap是可复用的内存块,而不是直接拿着用的Bitmap图片哦;
定义了引用队列,用来获取被GC回收的弱引用Bitmap对象;开启线程死循环,来不断的扫描这个引用队列;
package com.lk.bitmap_demo;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.util.LruCache;
import com.lk.bitmap_demo.disk.DiskLruCache;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* 管理内存中的图片
*/
public class ImageCache {
private static ImageCache instance;
private Context context;
//内存缓存
private LruCache<String,Bitmap> memoryCache;
//磁盘缓存
private DiskLruCache diskLruCache;
//用来做内存复用的
BitmapFactory.Options options=new BitmapFactory.Options();
/**
* 定义一个复用沲
*/
public static Set<WeakReference<Bitmap>> reuseablePool;
//单例
public static ImageCache getInstance(){
if(null==instance){
synchronized (ImageCache.class){
if(null==instance){
instance=new ImageCache();
}
}
}
return instance;
}
//引用队列
ReferenceQueue referenceQueue;
Thread clearReferenceQueue;
boolean shutDown;
// 获取引用队列
private ReferenceQueue<Bitmap> getReferenceQueue(){
if(null==referenceQueue){
//当弱用引需要被回收的时候,会进到这个队列中
referenceQueue=new ReferenceQueue<Bitmap>();
//单开一个线程,去扫描引用队列中GC扫到的内容,交到native层去释放
clearReferenceQueue=new Thread(new Runnable() {
@Override
public void run() {
while(!shutDown){
try {
//remove是阻塞式的
//获取到GC回收了弱引用的Bitmpa对象获取到
Reference<Bitmap> reference=referenceQueue.remove();
Bitmap bitmap=reference.get();
//如果bitmap没有回收释放,那么我们进行回收释放处理
if(null!=bitmap && !bitmap.isRecycled()){
bitmap.recycle();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
clearReferenceQueue.start();
}
return referenceQueue;
}
......
......
}
init方法
初始化工作中:初始化了复用池、初始化了内存缓存对象,并设定了缓存的最大值、初始化了磁盘缓存对象、开启了线程去循环扫描GC回收的弱引用对象,从引用队列中获取;
内存缓存对象的初始化,需要设置好缓存的最大值、重写了sizeOf、entryRemoved这两个方法,第一个方法是计算每个缓存的Bitmap对象所占大小,第二个方法是当Lru缓存满了之后,回调出来给我们自己处理的方法;
磁盘缓存对象的初始化,也给定了磁盘缓存最大的缓存空间;
//dir是用来存放图片文件的路径
public void init(Context context,String dir){
this.context=context.getApplicationContext();
//复用池
reuseablePool=Collections.synchronizedSet(new HashSet<WeakReference<Bitmap>>());
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
//获取程序最大可用内存 单位是M
int memoryClass=am.getMemoryClass();
// 内存缓存
//参数表示能够缓存的内存最大值 单位是byte
memoryCache=new LruCache<String,Bitmap>(memoryClass/8*1024*1024){
/**
* @return value占用的内存大小
*/
@Override
protected int sizeOf(String key, Bitmap value) {
//19之前 必需同等大小,才能复用 inSampleSize=1
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT){
return value.getAllocationByteCount();
}
return value.getByteCount();
}
/**
* 当lru满了,bitmap从lru中移除对象时,会回调
*/
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if(oldValue.isMutable()){//如果是设置成能复用的内存块,拉到java层来管理
//3.0以下 Bitmap native
//3.0以后---8.0之前 java
//8。0开始 native
//把这些图片放到一个复用沲中
reuseablePool.add(new WeakReference<Bitmap>(oldValue,referenceQueue));
}else{ //如果是不可服用的内存块,直接回调收
//oldValue就是移出来的对象
oldValue.recycle();
}
}
};
//valueCount:表示一个key对应valueCount个文件
try {
//初始化磁盘缓存对象
diskLruCache = DiskLruCache.open(new File(dir), BuildConfig.VERSION_CODE, 1, 10 * 1024 * 1024);
}catch(Exception e){
e.printStackTrace();
}
getReferenceQueue();
}
//将bitmap对象存入到内存缓存中,键值对方式存入
public void putBitmapToMemeory(String key,Bitmap bitmap){
memoryCache.put(key,bitmap);
}
//获取到内存缓存中存放的bitmap对象,通过key获取
public Bitmap getBitmapFromMemory(String key){
return memoryCache.get(key);
}
//清空内存缓存
public void clearMemoryCache(){
memoryCache.evictAll();
}
获取复用池中的Bitmap复用内存块
复用池中获取可用的复用内存块;
根据传入的宽高计算出内存,然后比对存放的内存块,看看是否又可用的复用内存块;
//获取复用池中,可用的复用内存块,如果没有的话会返回null
public Bitmap getReuseable(int w,int h,int inSampleSize){
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB){
return null;
}
Bitmap reuseable=null;
Iterator<WeakReference<Bitmap>> iterator = reuseablePool.iterator();
while(iterator.hasNext()){
Bitmap bitmap=iterator.next().get();
if(null!=bitmap){
//可以复用的内存块,取出来,并且从服用池中移除;
if(checkInBitmap(bitmap,w,h,inSampleSize)){
reuseable=bitmap;
iterator.remove();
break;
}
}
}
return reuseable;
}
//根据宽高计算所占内存,然后比对内存块,判断出是否可以使用当前这个存放的内存块进行复用
private boolean checkInBitmap(Bitmap bitmap, int w, int h, int inSampleSize) {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){
return bitmap.getWidth()==w && bitmap.getHeight()==h && inSampleSize==1;
}
if(inSampleSize>=1){
w/=inSampleSize;
h/=inSampleSize;
}
int byteCount=w*h*getPixelsCount(bitmap.getConfig());
return byteCount<=bitmap.getAllocationByteCount();
}
// 我们开头说到 ARGB_8888格式,一像素占用的是4个字节
//RGB_565格式,一像素占用的是2个字节
private int getPixelsCount(Bitmap.Config config) {
if(config==Bitmap.Config.ARGB_8888){
return 4;
}
return 2;
}
磁盘缓存的处理
存入磁盘缓存的时候,我们需要查看磁盘缓存中是否已经存放了,如果没有存放,才会存入到磁盘缓存中;
从磁盘缓存中获取的时候,会用可用的复用内存块进行加载这一个Bitmap对象,然后将从磁盘中获取到的Bitmap对象存放到内存缓存中,这样的话下次就能在内存缓存中直接获取到对应的Bitmap对象了,就能省去从磁盘缓存中加载Bitmap对象的这一步了;
//磁盘缓存的处理
/**
* 加入磁盘缓存
*/
public void putBitMapToDisk(String key,Bitmap bitmap){
DiskLruCache.Snapshot snapshot=null;
OutputStream os=null;
try {
snapshot=diskLruCache.get(key);
//如果缓存中已经有这个文件 不理他
if(null==snapshot){
//如果没有这个文件,就生成这个文件
DiskLruCache.Editor editor=diskLruCache.edit(key);
if(null!=editor){
os=editor.newOutputStream(0);
bitmap.compress(Bitmap.CompressFormat.JPEG,50,os);
editor.commit();
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null!=snapshot){
snapshot.close();
}
if(null!=os){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 从磁盘缓存中取
*/
public Bitmap getBitmapFromDisk(String key,Bitmap reuseable){
DiskLruCache.Snapshot snapshot=null;
Bitmap bitmap=null;
try {
snapshot=diskLruCache.get(key);
if(null==snapshot){
return null;
}
//获取文件输入流,读取bitmap
InputStream is=snapshot.getInputStream(0);
//解码个图片,写入
options.inMutable=true;
//这里也会用 可用的复用内存块进行加载
options.inBitmap=reuseable;
bitmap=BitmapFactory.decodeStream(is,null,options);
//从磁盘中获取到的图片Bitmap对象,我们会存放到内存缓存中
if(null!=bitmap){
memoryCache.put(key,bitmap);
}
} catch (IOException e) {
e.printStackTrace();
} finally{
if(null!=snapshot){
snapshot.close();
}
}
return bitmap;
}
完整代码
package com.lk.bitmap_demo;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.util.LruCache;
import com.lk.bitmap_demo;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* 管理内存中的图片
*/
public class ImageCache {
private static ImageCache instance;
private Context context;
//内存缓存
private LruCache<String,Bitmap> memoryCache;
//磁盘缓存
private DiskLruCache diskLruCache;
//用来做内存复用的
BitmapFactory.Options options=new BitmapFactory.Options();
/**
* 定义一个复用沲
*/
public static Set<WeakReference<Bitmap>> reuseablePool;
//单例
public static ImageCache getInstance(){
if(null==instance){
synchronized (ImageCache.class){
if(null==instance){
instance=new ImageCache();
}
}
}
return instance;
}
//引用队列
ReferenceQueue referenceQueue;
Thread clearReferenceQueue;
boolean shutDown;
// 获取引用队列
private ReferenceQueue<Bitmap> getReferenceQueue(){
if(null==referenceQueue){
//当弱用引需要被回收的时候,会进到这个队列中
referenceQueue=new ReferenceQueue<Bitmap>();
//单开一个线程,去扫描引用队列中GC扫到的内容,交到native层去释放
clearReferenceQueue=new Thread(new Runnable() {
@Override
public void run() {
while(!shutDown){
try {
//remove是阻塞式的
//获取到GC回收了弱引用的Bitmpa对象获取到
Reference<Bitmap> reference=referenceQueue.remove();
Bitmap bitmap=reference.get();
//如果bitmap没有回收释放,那么我们进行回收释放处理
if(null!=bitmap && !bitmap.isRecycled()){
bitmap.recycle();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
clearReferenceQueue.start();
}
return referenceQueue;
}
//dir是用来存放图片文件的路径
public void init(Context context,String dir){
this.context=context.getApplicationContext();
//复用池
reuseablePool=Collections.synchronizedSet(new HashSet<WeakReference<Bitmap>>());
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
//获取程序最大可用内存 单位是M
int memoryClass=am.getMemoryClass();
// 内存缓存
//参数表示能够缓存的内存最大值 单位是byte
memoryCache=new LruCache<String,Bitmap>(memoryClass/8*1024*1024){
/**
* @return value占用的内存大小
*/
@Override
protected int sizeOf(String key, Bitmap value) {
//19之前 必需同等大小,才能复用 inSampleSize=1
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT){
return value.getAllocationByteCount();
}
return value.getByteCount();
}
/**
* 当lru满了,bitmap从lru中移除对象时,会回调
*/
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if(oldValue.isMutable()){//如果是设置成能复用的内存块,拉到java层来管理
//3.0以下 Bitmap native
//3.0以后---8.0之前 java
//8。0开始 native
//把这些图片放到一个复用沲中
reuseablePool.add(new WeakReference<Bitmap>(oldValue,referenceQueue));
}else{ //如果是不可服用的内存块,直接回调收
//oldValue就是移出来的对象
oldValue.recycle();
}
}
};
//valueCount:表示一个key对应valueCount个文件
try {
//初始化磁盘缓存对象
diskLruCache = DiskLruCache.open(new File(dir), BuildConfig.VERSION_CODE, 1, 10 * 1024 * 1024);
}catch(Exception e){
e.printStackTrace();
}
getReferenceQueue();
}
//将bitmap对象存入到内存缓存中,键值对方式存入
public void putBitmapToMemeory(String key,Bitmap bitmap){
memoryCache.put(key,bitmap);
}
//获取到内存缓存中存放的bitmap对象,通过key获取
public Bitmap getBitmapFromMemory(String key){
return memoryCache.get(key);
}
//清空内存缓存
public void clearMemoryCache(){
memoryCache.evictAll();
}
//获取复用池中,可用的复用内存块,如果没有的话会返回null
public Bitmap getReuseable(int w,int h,int inSampleSize){
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB){
return null;
}
Bitmap reuseable=null;
Iterator<WeakReference<Bitmap>> iterator = reuseablePool.iterator();
while(iterator.hasNext()){
Bitmap bitmap=iterator.next().get();
if(null!=bitmap){
//可以复用的内存块,取出来,并且从服用池中移除;
if(checkInBitmap(bitmap,w,h,inSampleSize)){
reuseable=bitmap;
iterator.remove();
break;
}
}
}
return reuseable;
}
//根据宽高计算所占内存,然后比对内存块,判断出是否可以使用当前这个存放的内存块进行复用
private boolean checkInBitmap(Bitmap bitmap, int w, int h, int inSampleSize) {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){
return bitmap.getWidth()==w && bitmap.getHeight()==h && inSampleSize==1;
}
if(inSampleSize>=1){
w/=inSampleSize;
h/=inSampleSize;
}
int byteCount=w*h*getPixelsCount(bitmap.getConfig());
return byteCount<=bitmap.getAllocationByteCount();
}
// 我们开头说到 ARGB_8888格式,一像素占用的是4个字节
//RGB_565格式,一像素占用的是2个字节
private int getPixelsCount(Bitmap.Config config) {
if(config==Bitmap.Config.ARGB_8888){
return 4;
}
return 2;
}
//磁盘缓存的处理
/**
* 加入磁盘缓存
*/
public void putBitMapToDisk(String key,Bitmap bitmap){
DiskLruCache.Snapshot snapshot=null;
OutputStream os=null;
try {
snapshot=diskLruCache.get(key);
//如果缓存中已经有这个文件 不理他
if(null==snapshot){
//如果没有这个文件,就生成这个文件
DiskLruCache.Editor editor=diskLruCache.edit(key);
if(null!=editor){
os=editor.newOutputStream(0);
bitmap.compress(Bitmap.CompressFormat.JPEG,50,os);
editor.commit();
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null!=snapshot){
snapshot.close();
}
if(null!=os){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 从磁盘缓存中取
*/
public Bitmap getBitmapFromDisk(String key,Bitmap reuseable){
DiskLruCache.Snapshot snapshot=null;
Bitmap bitmap=null;
try {
snapshot=diskLruCache.get(key);
if(null==snapshot){
return null;
}
//获取文件输入流,读取bitmap
InputStream is=snapshot.getInputStream(0);
//解码个图片,写入
options.inMutable=true;
//这里也会用 可用的复用内存块进行加载
options.inBitmap=reuseable;
bitmap=BitmapFactory.decodeStream(is,null,options);
//从磁盘中获取到的图片Bitmap对象,我们会存放到内存缓存中
if(null!=bitmap){
memoryCache.put(key,bitmap);
}
} catch (IOException e) {
e.printStackTrace();
} finally{
if(null!=snapshot){
snapshot.close();
}
}
return bitmap;
}
}
如果对Gilde有兴趣了解的,可以看看我这篇;Android-图片加载Gilde
辛苦各位童鞋观看到最后,如果博客中有不对的地方望指出,大神勿喷,谢谢~~