美文网首页
利用LruCache实现双缓存加载图片

利用LruCache实现双缓存加载图片

作者: Zeller | 来源:发表于2016-05-08 21:36 被阅读397次

    双缓存实例

    • 下载DiskLruCache.java
      google认证的第三方
      https://android.googlesource.com/platform/libcore/+/android-4.1.1_r1/luni/src/main/java/libcore/io/DiskLruCache.java

    • 新建LruCacheUtils进行操作
      该类设计为单例模式。

        public class LruCacheUtils {
        private static LruCacheUtils lruCacheUtils;
      
        private DiskLruCache diskLruCache; //LRU磁盘缓存
        private LruCache<String, Bitmap> lruCache; //LRU内存缓存
        private Context context;
      
        private LruCacheUtils() {
        }
      
        public static LruCacheUtils getInstance() {
            if (lruCacheUtils == null) {
                lruCacheUtils = new LruCacheUtils();
            }
            return lruCacheUtils;
        }
      }```
      
      
    • 首先需要获得采样比例

      //获得采样比例
       public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
           //获取位图的圆宽高
           int width = options.outWidth;
           int height = options.outHeight;
           System.out.println("outWidth=" + width + "outHeight" + height);
           int inSampleSize = 1;
           if (width > reqWidth || height > reqHeight) {
               //判断原图的宽高,再进行匹配,按照小的来求采样比例
               if (width > height) {
                   inSampleSize = Math.round((float) height / (float) reqHeight);
               } else {
                   inSampleSize = Math.round((float) width / (float) reqWidth);
               }
           }
           System.out.println("inSampleSize" + inSampleSize);
           return inSampleSize;
       }
      
    • 再实现一个方法,通过字节转换成位图

      public Bitmap decodeSampleBitmapFromStream(byte[] bytes, int reqWidth, int reqHeight) {
      
           BitmapFactory.Options options = new BitmapFactory.Options();
           options.inJustDecodeBounds = true;
           BitmapFactory.decodeByteArray(bytes,0,bytes.length,options);
           options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
           options.inJustDecodeBounds = false;
           return BitmapFactory.decodeByteArray(bytes,0,bytes.length,options);
       }
      
    • 还需要两个方法,获取AppVersion以及缓存目录

      //在清单文件中定义的versionCode
        private int getAppVersion() {
      
            try {
                return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return 1;
        }
        //获取缓存目录
        private File getCacheDir(String name) {
            //判断是不是SD卡  或者外部存储已经被删掉   前面一个是有SD卡(mnt/sdcard/Android/data/包名/cache)   后面一个没有SD卡(缓存在私有目录下data/data/包名/cache)
            String cachePath = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED || !Environment.isExternalStorageRemovable()
                    ? context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();
            return new File(cachePath + File.separator + name);
      
        }
      
    • 下面是两个方法,计算MD5的字符串摘要

      //计算MD5的字符串摘要
      public String hashKeyForDisk(String key) {
        String cacheKey;
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(key.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(key.hashCode());
        }
        return cacheKey;
      

    }
    public 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();
    

    }

    * 然后是两个添加和删除内存缓存的方法
    

    //添加缓存
    public void addBitmapToCache(String url, Bitmap bitmap) {
    String key = hashKeyForDisk(url);
    if (getBitmapFromCache(key) == null) {
    System.out.println("key=====" + key);
    System.out.println("bitmap====" + bitmap);
    lruCache.put(key, bitmap);
    }
    }
    //读取缓存
    public Bitmap getBitmapFromCache(String url) {
    String key = hashKeyForDisk(url);
    return lruCache.get(key);
    }

    * 还需要打开缓存的方法
    

    public void open(Context context, String disk_cache_subdir, int disk_cache_size) {
    this.context = context;
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    int memoryClass = am.getMemoryClass();
    final int cacheSize = memoryClass / 8 * 1024 * 1024; //单位大小为字节 八分之一的内存作为缓存大小
    lruCache = new LruCache<>(cacheSize);
    try {
    diskLruCache = DiskLruCache.open(getCacheDir(disk_cache_subdir), getAppVersion(), 1, disk_cache_size);
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    
    * 下面是最重要的部分,加入缓存,这里用到了AsyncTask,而且还需要定义一个回调接口来做出完成的响应
    

    public void putCache(final String url, final CallBack callBack) {
    new AsyncTask<String, Void, Bitmap>() {
    @Override
    protected Bitmap doInBackground(String... params) {
    String key = hashKeyForDisk(params[0]);
    System.out.println("key=" + key);
    DiskLruCache.Editor editor = null;
    Bitmap bitmap = null;
    try {
    URL url = new URL(params[0]);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setReadTimeout(1000 * 30);
    conn.setConnectTimeout(1000 * 30);
    ByteArrayOutputStream baos = null;
    if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
    BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());
    baos = new ByteArrayOutputStream();
    byte[] bytes = new byte[1024];
    int len = -1;
    while ((len = bis.read(bytes)) != -1) {
    baos.write(bytes, 0, len);
    }
    bis.close();
    baos.close();
    conn.disconnect();
    }
    if (baos != null) {
    bitmap = decodeSampleBitmapFromStream(baos.toByteArray(), 100, 100);
    //加入缓存
    addBitmapToCache(params[0], bitmap);
    //加入磁盘缓存
    editor = diskLruCache.edit(key);
    System.out.println(url.getFile());
    //位图压缩后输出(参数:压缩格式,质量(100表示不压缩,30表示压缩70%),输出流)
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, editor.newOutputStream(0));
    editor.commit();
    }

               } catch (IOException e) {
                   try {
                       editor.abort();  //放弃写入
                   } catch (IOException e1) {
                       e1.printStackTrace();
                   }
                   e.printStackTrace();
               }
               return bitmap;
           }
           @Override
           protected void onPostExecute(Bitmap bitmap) {
               super.onPostExecute(bitmap);
               callBack.response(bitmap);
           }
       }.execute(url);
    

    }
    //回调接口
    public interface CallBack<T> {
    public void response(T entity);
    }

    
    * 最后还需要实现获取磁盘缓存,关闭磁盘缓存以及刷新磁盘缓存三个方法
    

    //获取磁盘缓存
    public InputStream getDiskCache(String url) {
    String key = hashKeyForDisk(url);
    System.out.println("getDiskCache=" + key);
    try {
    //快照
    DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
    System.out.println(snapshot);
    if (snapshot != null) {
    return snapshot.getInputStream(0);
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    return null;
    }
    //关闭磁盘缓存
    public void close() {
    if (diskLruCache != null && !diskLruCache.isClosed()) {
    try {
    diskLruCache.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    //刷新磁盘缓存
    public void flush() {
    if (diskLruCache != null) {
    try {
    diskLruCache.flush();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

    
    * 上面都是我们在LruCacheUtils.java的工具类中完成的方法,下面我们看看在MainActivity中的内容
    

    private DiskLruCacheUtils diskLruCacheUtils;
    private static final String DISK_CACHE_SUBDIR = "temp";//目录名
    private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; //定义磁盘缓存大小,10MB

    点击按钮时我们开始实现双缓存加载图片
    

    public void show2Click(View view) {
    String url = "http://p3.image.hiapk.com/uploads/allimg/141124/7730-141124100258.jpg";
    loadBitmap(url, iv2);
    }

    * 我们需要在onResume方法中打开磁盘缓存,在onPause时刷新缓存,并在onStop时关闭缓存
    

    @Override
    protected void onResume() {
    super.onResume();
    lruCacheUtils = LruCacheUtils.getInstance();
    lruCacheUtils.open(this, DISK_CACHE_SUBDIR, DISK_CACHE_SIZE);
    }
    //刷新把缓存写好
    @Override
    protected void onPause() {
    super.onPause();
    lruCacheUtils.flush();
    }
    @Override
    protected void onStop() {
    super.onStop();
    lruCacheUtils.close();
    }

    
    * 最后我们需要实现loadBitmap方法,加载图片
    

    public void loadBitmap(String url, final ImageView imageView) {
    //从内存缓存中取图片
    Bitmap bitmap = lruCacheUtils.getBitmapFromCache(url);
    if (bitmap == null) {
    //再从磁盘缓存中取
    InputStream in = lruCacheUtils.getDiskCache(url);
    if (in == null) {
    //去网上取
    lruCacheUtils.putCache(url, new LruCacheUtils.CallBack<Bitmap>() {
    @Override
    public void response(Bitmap entity) {
    System.out.println("http load");
    imageView.setImageBitmap(entity);
    }
    });
    } else {
    System.out.println("disk cache");
    bitmap = BitmapFactory.decodeStream(in);
    //取完添加到内存缓存中
    lruCacheUtils.addBitmapToCache(url, bitmap);
    imageView.setImageBitmap(bitmap);
    }
    } else {
    System.out.println("memory cache");
    imageView.setImageBitmap(bitmap);
    }
    }

    相关文章

      网友评论

          本文标题:利用LruCache实现双缓存加载图片

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