Bitmap的加载和Cache

Bitmap在Android中指的是一张图片,可以是png格式和jpg格式,

Bitmap加载图片的方法,有BitmapFactory提供了 decodeFile、decodeResource、decodeStream、decoceByteArray,分别用于支持从文件系统、资源、输入刘、字节数组中加载一个Bitmap对象

如何高效地加载Bitmap

采用BitmapFactory.Options来加载所需尺寸的图片,通过BitmapFactory.Options可以按照一定的采样率来加载缩小后的图片。BitmapFactory提供的加载图片的四类方法都支持BitmapFactory,Options参数。通过BitmapFactory.Options来缩放图片主要是用到了他的inSampleSize参数,当inSampleSize为1时,采样后的图片大小为图片的原始大小。为2时,采样后图片的宽高均为原始图片的1/2,而像素为原图的1/4。采样率inSampleSize必须是大于1的整数才有缩小效果,如果小于1,其作用相当于1,另外最新的官方文档指出,inSampleSize的取值应该是为2的指数

获取采样率

(1)将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片

(2)从BitmapFactory.Options中取出图片的原始宽高信息,他们对应于outWidth和outHeigth参数

(3)根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize

(4)将BitmapFactory.Option的inJustDecodeBounds参数设为false

inJustDecodeBounds参数,当此参数设为true是,BitmapFactory只会解析图片的原始宽高信息,并不会真正的加载图片

注意,这个时候BitmapFactory获取图片的宽高信息和图片的位置以及程序运行的设备有关

eg

public static Bitmap decodeSampledBitmapFromResource(Resource res ,int resId,int reqWidth,int reHeight){

    final BitmapFactory.Options options = new BitmapFactory.Option();

    options.inJustDecodeBounds = true;

    BitmapFactory.decodeResource(res,resId,option);

    options.inSampleSize = calculateInSampleSize(Options,reqWidth,resHeigth);

    options.inJustDecodeBounds = false;

    return BitmapFactory.decodeResource(res,resId,options);

}

public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){

    final int height = options.outHeight;

    final int width = optionts.outWidth;

    int inSampelSize = 1;

    if(heigth>reqHeigth || width > reqWidth){

        final int  halfHeigth = heigth/2;

        final int halfWidth = width/2;

        while((halfHeigth/inSampleSize) >= reqHeigth && (halfWidth/inSampleSize) >= reqWidth){

            inSampleSize *=2;

        }

    }

    return inSampleSize;

}

Android中的缓存策略

LurCache

LurCache是一个泛型类,它内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象,其提供了get和put方法来完成缓的获取和添加操作,当缓存满时,LurCache会移除较早使用的缓存对象,然后再添加新的缓存对象

eg

创建LurCache

int maxMemory = (int) (Runntime.getRuntime().maxMemory()/1024);

int cacheSize = maxMemory/8;

mMemory = new LurCache<String,Bitmap>(cacheSize){

    @override

     protected int sizeOf(String key, Bitmap bitmap){//计算缓存对象的大小

           return bitmap.getRowBytes()*bitmap.getHeigth()/1024;

    }

};

一些特殊的情况,还需要重写LurCache的entryRemoved方法,LurCache移除旧缓存时会调用entryRemoved方法,因此可以在该方法中完成一些资源回收工作

从LurCache中获取缓存对象

mMemory.get(key)

向LurCache添加对象

mMemory.put(key,bitmap)

DisLurCache

DisLurCache用于实现设备缓存,即磁盘缓存

DisLurCache的创建

public static DisLurCache open(File directory,int appVersion,int valueCount, long maxSize)

参数

directory:磁盘缓存的文件系统中的存储路径。缓存路径可以选择SD卡上的缓存目录,具体指/sdcard/Android/data/package_name/cache目录,其中package_name表示当前应用的包名,当应用被卸载后,此目录会一并被删除,当然也可以选择SD卡上的其他指定目录,建议,如果应用卸载后就希望删除缓存文件,那么就选择SD卡上的缓存目录

appVersion:表示应用的版本号,一般设置为1,当版本好发生改变时DisLurCache会清空之前的所有的缓存文件

valueCount:表示单个节点岁对应的数据个数,一般设置为1

maxSize:表示缓存的总大小,当缓存大小超出这个设定值后,DisLurCache会清除一些缓存从而保证总大小不大于这个设定值

eg

创建过程

private static final long DISK_CACHE_SIZE = 1024*1024*50;

File dislCacheDir = getDiskCacheDir(mContext,"bitmap");

if(!disCacheDir.exist()){

    diskCacheDir.mKdirs();

}

mDiskLruCache = DiskLruCache.open(disCacheDir,1,1,DISK_CACHE_SIZE );

DiskLruCache的缓存添加

DiskLruCache的缓存添加的操作是通过Editor完成的,Editor表示一个缓存对象的编辑对象,这个用图片缓存举例,首先需要获取图片url所对应的key,然后根据Key就可以通过edit()来获取Editor对象,如果缓存对象正在被编辑,那么edit()会返回null,即DiskLruCache不允许同时编辑一个缓存对象

private String hashKeyFromFormUrl(String url){

    String cacheKey;

    try{

            final MessageDigset mDigest = MessageDigest.getInstance("MD5");

            mDigest.update(url.getBytes());

            cacheKey = bytesToHexString(mDigest.digest());

        }catch(NoSuchAlgorithmException){

            cacheKey = String.valueOf(url.hashCode());

        }

        return cacheKey;

}

private String bytesToHexString(byte[] bytes){

    StringBuilder sb = new StringBuilder();

    for(int i = 0;i<bytes.length;i++){

        String hex = Integer.toHexString(0xFF & bytes[i])

        if(hex.length() == 1){

            sb.append('0');

         }

        sb.append(hex);

    }

    return sb.toString();

}


String key = hashKeyFormUrl(url);

DiskLruCache.Editor editor = mDiskLruCache.edit(key);

if(editor != null){

    OutputStream outputStream = editor.new OutputStream(DISK_CACHE_INDEX);

}


public boolean downloadUrlToStream(String urlString,OutputStream outputStrean){

    HttpUrlConnect urlConnect = null;

    BufferedOutputStream out = null;

    BufferedInputStream in = null;

    try{

        final URL  url = new Url(urlStriing);

        urlConnect = (HttpUrlConnect )tpurl.openConnect();

        in = new BufferedInputStream(urlConnect.getInputStream,IN_BUFFER_SIZE);

        out = new BufferedOutputStream(outputStream,IN_BUFFER_SIZE);

        int b;

        while((b = in.read()) != -1){

            out.write(b);

        }

        return true;

    }catch(IOException e){

        

    }finally{

        if(urlConnect != null){

            urlConnect.disconnect();

        }

    }

    reture false;

}


OutputStream outputStream = editor.newOutputStream(DISL_CACHE_INDEX);

if(downloadUrlToStream(url,outputStream)){

    editor.commit();

}else{

    editor.abort();

}

mDiskLruCache.flush();


DiskLruCache的查找

使用BitmapFactory.Options对像加载缩放图,这种方法对FileInputStream的缩放存在问题,原因是FileInputStream是一种有序的文件流,而两次decodeStream调用与影响了文件流的位置属性,导致第二次decodeStream时得到的是null;解决方法,,可以通过文件流得到所对应的文件描述符,然后再通过BitmapFactory.decodeFileDescriptor方法

来加载一张缩放后的图片。

Bitmap bitmap = null;

String key = hashKeyFormUrl(url);

DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);s

if(snapshort != null){

    FileInputStream fileInputStream = (FileInputStream )snapshot.getInputStream(DISK_CACHE_INDEX);

    FileDescriptor fileDescriptor =  fileInputStream.getFD();

    bitmap = mImageResizer.decodeSampledBitmapFrimFileDescriptor(fileDescriptor,reqWidth,reqHeigth);

    if(bitmap != null){

        addBitmapToMemoryCache(key,bitmap);

    }

}

你可能感兴趣的:(Bitmap的加载和Cache)