美文网首页
LRUCache(内存)DiskLruCache (硬盘)缓存的

LRUCache(内存)DiskLruCache (硬盘)缓存的

作者: 君袅 | 来源:发表于2019-02-18 23:18 被阅读0次

    了解

    什么是LruCache?

    LruCache是Android在API12上引入的一种缓存机制,这种缓存有个特点,就是当缓存容量达到上限时,会将最近最少使用的对象从cache中清除出去。

    那么这种缓存方式跟普通的缓存有什么区别呢?为什我需要使用它?

    曾经,在各大缓存图片的框架没流行的时候。有一种很常用的内存缓存技术:SoftReference 和 WeakReference(软引用和弱引用),但是走到了 Android 2.3(Level 9)时代,垃圾回收机制更倾向于回收 SoftReference 或 WeakReference 的对象。后来,又来到了Android3.0,图片缓存在native中,因为不知道要在是什么时候释放内存,没有策略,没用一种可以预见的场合去将其释放,这就造成了内存溢出。
    所以LruCache的出现就是为了解决上述问题,它使用强引用来缓存,消除了垃圾回收机制的影响,同时通过设定缓存上限以及著名的LRU算法来管理内存,使得内存的管理变得更加高效和灵活。

    如何使用LruCache?

    LruCache是通过LinkedHashMap来实现LRU内存管理的,所以我们可以将其当作一个map来使用,如put,get,remove等。但是要注意,LruCache是不允许key和value为空的。
    接下来是针对LruCache这个知识点的一些延伸

    Lru是什么?

    LRU是操作系统内存管理中比较经典的一种缓存淘汰算法,相信学过操作系统的童鞋应该都有耳闻。操作系统里面还存在一些其他的缓存淘汰算法,如LIFO,FIFO,LFU等,它们都是从某一个维度来评估缓存中条目的重要性,从而当缓存不足时淘汰掉其中最不重要的条目。可见当我们在做App开发时,操作系统里面很多的技术思想其实也可以拿来做参考。
    LinkedHashMap为什么能实现LruCache中的Lru?
    LinkedHashMap跟HashMap一个重要区别就是其内部的entry是有序的,它有两种排序规则:插入排序和访问排序,其实也就是按put调用时间排序还是按get调用时间来排序。LinkedHashMap通过设置访问排序来是实现Lru的。

    LRUCache缓存一张图片

    1.建立一个类(图片通过这个类进行缓存)

    public class MemCache {
        private LruCache<String, Bitmap> mImageCache;
        // 初始化 - 构造
        public MemCache() {
            initCache();
        }
        private void initCache() {
            // 计算可使用的最大内存
            final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
            // 取4分之一的可用内存作为缓存
            final int cacheSize = maxMemory / 4;
            mImageCache = new LruCache<String, Bitmap>(cacheSize) {
                @Override
                protected int sizeOf(String key, Bitmap bitmap) {
                    return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
                }
            };
        }
        /**
         * 保存数据--bitmap
         * @param url  图片的name
         * @param bitmap
         */
        public void putBitmap(String url, Bitmap bitmap) {
            if (mImageCache != null) {
                mImageCache.put(url, bitmap);
            }
        }
        //获取缓存资源
        public Bitmap getBitmap(String url){
            return mImageCache.get(url);
        }
    }
    

    2.在activity中实现缓存

    public class MainActivity extends AppCompatActivity {
    
        private String url = "http://cms-bucket.ws.126.net/2019/02/15/97392c98fe22485a8a2458ba9dd907d9.jpeg";
        private ImageView img;
        private MemCache memCache=new MemCache();
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
        }
        @Override
        protected void onStart() {
            super.onStart();
                    /*
                    * 1.在activity开始就要实现
                    * 2.首先获取缓存资源判断是否为空
                    * 3.如果为空就请求网络缓存
                    * 4.如果不为空就用缓存的数据
                    * */
                    Bitmap bitmap = memCache.getBitmap(url);
                    if(bitmap==null){
                        initData();
                    }else {
                        img.setImageBitmap(bitmap);
                    }
        }
    
        private void initData() {
            //1.创建OkHttpClient对象
            OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
            //2.创建Request请求对象
            Request build = new Request.Builder()
                    .get()
                    .url(url)
                    .build();
            //3.创建Call对象
            Call call = okHttpClient.newCall(build);
            //4.异步
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                }
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    //通过BitmapFactory把加载成bitmap
                    final Bitmap bitmap = BitmapFactory.decodeStream(response.body().byteStream());
                    if(memCache.getBitmap(url)!=null){
                        memCache.putBitmap(url,bitmap);
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            img.setImageBitmap(bitmap);
                        }
                    });
                }
            });
        }
        private void initView() {
            img = (ImageView) findViewById(R.id.img);
        }
    }
    

    DiskLruCache 是缓存在硬盘中的

    依赖

        implementation 'com.jakewharton:disklrucache:2.0.2'
    

    给字符加密一些准备措施的类

    public class Utils {
        private static final String TAG = "Utils";
        public File getDiskCacheDir;
    
        /**
         * 获取缓存文件夹,这里优先选择SD卡下面的android/data/packageName/cache/路径,若没有SD卡,就选择data/data/packageName/cache
         *
         * @param context    上下文环境
         * @param uniqueName 缓存文件夹名称
         * @return 返回缓存文件
         */
        public static File getDiskCacheDir(Context context, String uniqueName) {
            String cachePath;
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                    || !Environment.isExternalStorageRemovable()) {
                cachePath = context.getExternalCacheDir().getPath();
            } else {
                cachePath = context.getCacheDir().getPath();
            }
            File file = new File(cachePath + File.separator + uniqueName);
            Log.d(TAG, "getDiskCacheDir: file="+file.getAbsolutePath());
            return file;
        }
        /**
         * 获取本App的版本号
         *
         * @param context context上下文
         * @return 返回版本号
         */
        public static int getAppVersion(Context context) {
            try {
                PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
                return info.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 1;
        }
        /**
         * 给字符串来个md5加密,
         * @param key 需要加密的string
         * @return 返回加密后的string ,或者加密失败,就返回string的哈希值
         */
        public static String hashKeyForDisk(String key) {
            String cacheKey;
            try {
                //md5加密
                MessageDigest mDigest = MessageDigest.getInstance("MD5");
                mDigest.update(key.getBytes());
                cacheKey = bytesToHexString(mDigest.digest());
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
                //若md5加密失败,就用哈希值
                cacheKey = String.valueOf(key.hashCode());
            }
            return cacheKey;
        }
        /**
         * 字节数组转为十六进制字符串
         * @param bytes 字节数组
         * @return 返回十六进制字符串
         */
        private static String bytesToHexString(byte[] bytes) {
            StringBuilder sb = new StringBuilder();
            for (byte b : bytes) {
                String hex = Integer.toHexString(0xFF & b);
                if (hex.length()==1){
                    sb.append('0');
                }
                sb.append(hex);
            }
            return sb.toString();
        }
    
    }
    

    建立一个操控缓存对象的类

    public class DiskCache {
        private static final String TAG = "DiskCache";
        DiskLruCache mDiskLruCache;
        // 初始化
    
        public DiskCache(File directory, long maxSize) {
            try {
                mDiskLruCache = DiskLruCache.open(directory, 1, 1, maxSize);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 保存bitmap到磁盘
         * 通过图片url 然后md5 加密的内容,作为key  来保存bitmap 对象
         *
         * @param url    图片请求url  --》 md5  去除特殊字符
         * @param bitmap
         */
        public void saveBitmap(String url, Bitmap bitmap) {
    
            try {
                String key = Utils.hashKeyForDisk(url);
                //editor 操作数据保存逻辑
                DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                OutputStream os = editor.newOutputStream(0);
                //此处存的一个 bitmap 对象因此用 ObjectOutputStream
                ObjectOutputStream outputStream = new ObjectOutputStream(os);
                // 下载图片
                if (downloadImage(url, outputStream)) {
                    editor.commit();
                } else {
                    editor.abort();
                }
                mDiskLruCache.flush();
                //别忘了关闭流和提交编辑
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 通过url  获取md5加密后的key。然后通过key 获取bitmap 对象
         *
         * @param url
         * @return
         */
        public Bitmap getBitmap(String url) {
            //使用DiskLruCache获取缓存,需要传入key,而key是imageUrl加密后的字符串,
            Bitmap bitmap = null;
            String key = Utils.hashKeyForDisk(url);
            //通过key获取的只是一个快照
            DiskLruCache.Snapshot snapshot = null;
            try {
                snapshot = mDiskLruCache.get(key);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (snapshot != null) {
                InputStream inputStream = snapshot.getInputStream(0);//类似写缓存时候,传入的是缓存的编号
                //可以使用bitmapFactory
                bitmap = BitmapFactory.decodeStream(inputStream);
            }
    
            return bitmap;
        }
    
    
        /**
         * 下载图片
         *
         * @param imgUrl       图片网址链接
         * @param outputStream 输出流对象
         * @return 返回时候完成下载成功
         */
        private boolean downloadImage(String imgUrl, OutputStream outputStream) {
            HttpURLConnection urlConnection = null;
            BufferedOutputStream out = null;
            BufferedInputStream in = null;
            try {
                URL url = new URL(imgUrl);
                urlConnection = (HttpURLConnection) url.openConnection();
                in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);//Buffer输入流,8M大小的缓存
                out = new BufferedOutputStream(outputStream, 8 * 1024);
                int b;//正在读取的byte
                while ((b = in.read()) != -1) {
                    out.write(b);
                }
                return true;
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            //关闭资源
            finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
                if (out != null) {
                    try {
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return false;
        }
    }
    

    在activity中的使用

    public class Main2Activity extends AppCompatActivity {
    
        private String imgUrl = "http://cms-bucket.ws.126.net/2019/02/15/97392c98fe22485a8a2458ba9dd907d9.jpeg";
        private ImageView mImg;
        DiskCache diskCache;
        private static final String TAG = "MainActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
            // 初始化硬盘缓存对象
            diskCache = new DiskCache(Utils.getDiskCacheDir(this,"cache"),10*1024);
    
            /**
             * 动态获取权限,Android 6.0 新特性,一些保护权限,除了要在AndroidManifest中声明权限,还要使用如下代码动态获取
             */
            if (Build.VERSION.SDK_INT >= 23) {
                int REQUEST_CODE_CONTACT = 101;
                String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE,WRITE_SECURE_SETTINGS};
                //验证是否许可权限
                for (String str : permissions) {
                    if (this.checkSelfPermission(str) != PackageManager.PERMISSION_GRANTED) {
                        //申请权限
                        this.requestPermissions(permissions, REQUEST_CODE_CONTACT);
                        return;
                    }
                }
            }
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            if (diskCache.getBitmap(imgUrl)!=null){
                mImg.setImageBitmap(diskCache.getBitmap(imgUrl));
            }else{
                requestNetBitmap();
            }
        }
    
        private void initView() {
            mImg = (ImageView) findViewById(R.id.img);
        }
        /**
         * 请求网络的图片对象
         */
        private void requestNetBitmap() {
    
            OkHttpClient okHttpClient = new OkHttpClient();
            Request request = new Request.Builder()
                    .get()
                    .url(imgUrl)
                    .build();
            Call call = okHttpClient.newCall(request);
    
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    Log.d(TAG, "onFailure: ");
                }
                @Override
                public void onResponse(Call call, final Response response) throws IOException {
    //                final byte[] bytes = response.body().bytes();
                    final InputStream inputStream = response.body().byteStream();
                    final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                    if (bitmap != null) {
                        // 保存磁盘数据
                        diskCache.saveBitmap(imgUrl,bitmap);
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mImg.setImageBitmap(bitmap);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            });
        }
    }
    

    相关文章

      网友评论

          本文标题:LRUCache(内存)DiskLruCache (硬盘)缓存的

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