美文网首页Android文章
Android小知识-Android中的缓存策略(内存缓存和磁盘

Android小知识-Android中的缓存策略(内存缓存和磁盘

作者: 爱读书的顾先生 | 来源:发表于2019-01-15 19:03 被阅读620次

    本篇文章已授权微信公众号 顾林海 独家发布

    通过缓存策略,我们不需要每次都从网上请求图片或从存储设备中加载图片,这样就极大地提高了图片的加载效率以及用户体验。目前比较常用的缓存策略是LruCache和DiskLruCache,其中LruCache被称为内存缓存,DiskLruCache被称为存储缓存。Lru是Least Recently Used的缩写,即最近最少使用算法,算法核心思想是:当缓存快满时,会淘汰近期最少使用的缓存目标。

    缓存路径:内存->存储设备->网络

    LruCache

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

    LruCache是线程安全的。

    • 强引用、软引用和弱引用的区别:

    • 强引用:直接的对象引用。

    • 软引用:当一个对象只有软引用存在时,系统内存不足时此对象会被GC回收。

    • 弱引用:当一个对象只有弱引用存在时,此对象随时被GC回收。

    LruCache初始化代码:

    private LruCache<String,Bitmap> mMemoryCache;
    private void lruCache(){
        //当进程可用的最大内存(KB)
        int maxMemory= (int) (Runtime.getRuntime().maxMemory()/1024);
        //LruCache缓存的总容量
        int cacheSize=maxMemory/8;
        mMemoryCache=new LruCache<String, Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes()*value.getHeight()/1024;
            }
        };
    }
    

    重写sizeOf方法,用来计算缓存对象的大小。

    在一些特殊情况下,还需要重写LruCache的entryRemoved方法,LruCache移除旧缓存时会调用entryRemoved方法,可以在entryRemoved方法中做一些资源回收工作。

    DiskLruCache

    DiskLruCache用于实现存储设备缓存,即磁盘缓存,它通过将缓存对象写入文件系统从而实现缓存的效果。

    引入:
    compile 'com.jakewharton:disklrucache:2.0.2'

    DiskLruCache不能通过构造方法来创建,它提供了open方法用于创建自身,如下代码:

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

    open方法有四个参数,第一个参数表示磁盘缓存在文件系统中的存储路径。缓存路径可以选中SD卡上的缓存目录,具体是指/sdcard/Android/data/package_name/cache 目录,当应用被卸载后,此目录会一并删除,也可以存储在其他指定目录。

    第二个参数表示应用的版本号,一般设为1即可。当版本号发生改变时DiskLruCache会清空之前所有的缓存文件。

    第三个参数表示单个节点所对应的数据的个数,一般设为1即可。

    第四个参数表示缓存的总大小。

    DiskLruCache的创建:

    private DiskLruCache mDiskLruCache;
    private static final long DISK_CACHE_SIZE=1024*1024*50;
    private void diskLruCache(){
        File diskCacheDir=new File(getCacheDir().getPath()+"/bitmap");
        if(!diskCacheDir.exists()){
            diskCacheDir.mkdirs();
        }
        try {
            mDiskLruCache=DiskLruCache.open(diskCacheDir,1,1,DISK_CACHE_SIZE);
        } catch (IOException e) {
            e.printStackTrace();
        }
    
    }
    

    DiskLruCache的缓存添加的操作是通过Editor完成的,Editor表示一个缓存对象的编辑对象。

    DiskLruCache的缓存添加:

    private void diskCache(String url){
        String key=hashKeyFormUrl(url);
        try {
            DiskLruCache.Editor editor=mDiskLruCache.edit(key);
            if(editor!=null){
                OutputStream outputStream=editor.newOutputStream(0);
                if(downloadUrlToStream(url,outputStream)){
                    editor.commit();
                }else{
                    editor.abort();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private String hashKeyFormUrl(String url){
        String cacheKey;
        try {
            final MessageDigest messageDigest=MessageDigest.getInstance("MD5");
            messageDigest.update(url.getBytes());
            cacheKey=bytesToHexString(messageDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey=String.valueOf(url.hashCode());
        }
        return cacheKey;
    }
    
    private String bytesToHexString(byte[] bytes){
        StringBuilder stringBuilder=new StringBuilder();
        for(int i=0,length=bytes.length;i<length;i++){
            String hex=Integer.toHexString(0xFF&bytes[i]);
            if(hex.length()==1){
                stringBuilder.append('0');
            }
            stringBuilder.append(hex);
        }
        return stringBuilder.toString();
    }
    
    public boolean downloadUrlToStream(String url, OutputStream outputStream){
        //进行网络请求,并将数据写入outputStream
        return true;
    }
    

    DiskLruCache的缓存查找

    缓存查找过程也需要将url转换为key,然后通过DiskLruCache的get方法得到一个Snapshot对象,接着再通过Snapshot对象即可得到缓存的文件输入流,有了输入流就可以得到Bitmap对象。

    private void getDiskCache(String url){
        Bitmap bitmap=null;
        String key=hashKeyFormUrl(url);
        try {
            DiskLruCache.Snapshot snapshot=mDiskLruCache.get(key);
            if(snapshot!=null){
                FileInputStream fileInputStream= (FileInputStream) snapshot.getInputStream(0);
                FileDescriptor fileDescriptor=fileInputStream.getFD();
                bitmap=decodeSampleBitmapFromFileDescriptor(fileDescriptor,500,500);
                if(bitmap!=null){
                    //显示图片或是将bitmap进行内存缓存
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static Bitmap decodeSampleBitmapFromFileDescriptor(FileDescriptor fileDescriptor,int reqWidth,int reqHeight){
        final BitmapFactory.Options options=new BitmapFactory.Options();
        options.inJustDecodeBounds=true;
        BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
        options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight);
        options.inJustDecodeBounds=false;
        return BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
    }
    
    public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
        int outWidth=options.outWidth;
        int outHeight=options.outHeight;
        int inSampleSize=1;
        if(outHeight>reqHeight||outWidth>reqWidth){
            final int halfHeight=outHeight/2;
            final int halfWidth=outWidth/2;
            while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
                inSampleSize*=2;
            }
        }
        return inSampleSize;
    }
    

    关于LruCache和DiskLruCache的内部实现会在后续文章中给出
    (ps:希望大家能关注下我的公众号,毕竟写作不易,可以通过公众号和我交流一些技术,还有大家对Android有哪方面想知道的可以在底下评论,我会选些支持率高的内容进行讲解。


    838794-506ddad529df4cd4.webp.jpg

    相关文章

      网友评论

        本文标题:Android小知识-Android中的缓存策略(内存缓存和磁盘

        本文链接:https://www.haomeiwen.com/subject/hfxudqtx.html