频道栏目
首页 > 资讯 > Android > 正文

(Android)之三级缓存(及封装方法)

16-10-22        来源:[db:作者]  
收藏   我要投稿

为什么使用三级缓存?

在当今4G网路的时代中,浏览网页的越来越快,随之而来的就是流量不够用,那么各种app都在往省流量的方向上走着,如果你的app没有缓存,那么用户往回浏览信息又会再刷新一次数据,这样就背道而驰了。所以有了三级缓存的机制了。 安卓有一个解决的方法,就是使用LRUCache。 什么是LRUCache?意思为最近最少使用算法的缓存

三级缓存帮助类及其详解

1、首先得理解什么是三级缓存? 我们希望的程序打开一个app加载图片的方法为: 内存--->磁盘-->网络 存入内存的方法有,使用LruCache,而磁盘存储安卓没有专门的类可以使用,但是在github上,google公司也默认的可行的方法类,今天我们将用到这个类:DiskLruCache 我这里有两个链接:一个是源码,一个是jar

2、前期准备工作已完成,那么开始写我们的帮助类吧

package com.sdp.panda.pictrueapp;

import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.LruCache;

import com.jakewharton.disklrucache.DiskLruCache;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Created by 80926 on 2016/10/19.
 * 包涵内存和磁盘缓存
 */

public class LruCacheUtils {
    private static LruCacheUtils instance;//单例模式
    private LruCache lruCache;
    private Context context;
    private DiskLruCache diskLruCache;

    private LruCacheUtils() {}

    public static LruCacheUtils getInstance() {
        if (instance == null) {
            instance = new LruCacheUtils();
        }
        return instance;
    }

    //打开磁盘缓存
    public void open(Context context, String disk_cache_subdir, int dis_cache_size) {
        try {
            this.context = context;
            ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            int memoryClass = manager.getMemoryClass();
            lruCache = new LruCache<>((memoryClass / 8) * 1024 * 1024);//获得内存缓存空间为给定的内存的1/8,
            /**
             * getAppVersion():获取版本的时候,就会清除缓存
             *               1:为一个key存多少个类型的缓存,磁盘的大小
             *  dis_cache_size:自己存储的大小,通常为10M
             */
            diskLruCache = DiskLruCache.open(getCacheDir(disk_cache_subdir), getAppVersion(), 1, dis_cache_size);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //缓存文件
    private File getCacheDir(String name) {
        //如果有sd卡,那么创建在外部储存mnt/android/data/packageame/cache/name ,
        // 否则创建在内部储存dada/data/package/cache/name中(最好为1m)
        String cachePath = (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED || !Environment.isExternalStorageRemovable() ?
                context.getExternalCacheDir().getPath() : context.getCacheDir().getPath());
        return new File(cachePath + File.separator + name);
    }

    //版本信息
    private int getAppVersion() {
        try {
            return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return 1;
    }
    //根据MD5计算出来的字符串,根据下载的地址转换成MD5的新字符串
    public String hashKeyForDisk(String url){
        String cacheKey;
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");//计算摘要 16进制的符号
            digest.update(url.getBytes());
            cacheKey = bytesToHexString(digest.digest());
        }catch (NoSuchAlgorithmException e){
            cacheKey = String.valueOf(url.hashCode());
        }
        return cacheKey;
    }
    public String bytesToHexString(byte[] digests){
        StringBuilder sb = new StringBuilder();
        for(int i = 0 ; i < digests.length ; i ++){
            String hex = Integer.toHexString(0xFF&digests[i]);
            if (hex.length() ==1){
                sb.append("0");
            }
            sb.append(hex);
        }
        return sb.toString();
    }
    /**
     * 下载图片到内存和磁盘
     * 使用到下载就需要异步任务完成
     */
    public void fromNetToCache(String url, final int reqWidth, final int reqHeight, final Callback callback){
        new AsyncTask(){

            @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&&conn!=null){
                        BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());
                        baos = new ByteArrayOutputStream();
                        byte[] buffer = new byte[1024];
                        int len = -1;
                        while ((len = bis.read(buffer))!=-1){
                            baos.write(buffer,0,len);
                            baos.flush();
                        }
                        bis.close();
                        baos.close();
                        conn.disconnect();
                    }
                    if (baos!=null){
                        bitmap = decodeSampleBitmapFromStream(baos.toByteArray(),reqWidth,reqHeight);//缩小以后的位图
                        addBitmapToCache(params[0],bitmap);//添加到缓存种
                        editor = diskLruCache.edit(key);//添加到磁盘
                        System.out.println(url.getFile());
                        //这个方法是将文件存在输出流当中,并且可以压缩,这里只是操作一张图片所以为 0
                        //这里的100为不压缩,70的话是压缩30%
                        bitmap.compress(Bitmap.CompressFormat.JPEG,100,editor.newOutputStream(0));
                        editor.commit();
                    }
                }catch (IOException e){
                   try {
                       editor.abort();
                   }catch (IOException ie){
                       ie.printStackTrace();
                   }
                }
                return bitmap;
            }

            @Override
            protected void onPostExecute(Bitmap bitmap) {
                super.onPostExecute(bitmap);
                callback.response(bitmap);
            }
        }.execute(url);
    }
        //从磁盘中取
        public InputStream getDiskCache(final String url){
            String key = hashKeyForDisk(url);
            System.out.println("diskKey::::"+key);
            try {
                DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
                if (snapshot!=null){
                    InputStream is = snapshot.getInputStream(0);
                    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();
            }
        }
    }
    //回掉接口
    public interface Callback{
        void response(Bitmap entity);
    }

    /**
     * 以下的是往内存中存储
     */
    //计算所有变换的比例,采样率
    public  int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
        int height = options.outHeight;
        int width = options.outWidth;
        int inSampleSize = 1;
        if (height>reqHeight||width>reqWidth){
            if (width>height){
                inSampleSize = Math.round((float)height/(float)reqHeight);
            }else {
                inSampleSize = Math.round((float)width/(float)reqWidth);
            }
        }
        return inSampleSize;
    }
    //从网路中得到的字节流,得到需求的bitmap
    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);
    }
    //从本地图片中得到得到最新位图
    public  Bitmap decodeSampleBitmapFromResource(Resources resources, int resId, int reqWidth, int reqHeight){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;//只是获取它的属性不显示图片
        BitmapFactory.decodeResource(resources,resId,options);
        options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight);
        options.inJustDecodeBounds = false;//不获取属性,
        return BitmapFactory.decodeResource(resources,resId,options);
    }
    //LRU缓存,往内存中添加位图
    public  void addBitmapToCache(String url,Bitmap bitmap){
        String key = hashKeyForDisk(url);
        if (getBitmapFromCache(key)==null){
            lruCache.put(key,bitmap);
        }
    }
    //从LRU内存中取出要用的key对应的bitmap
    public  Bitmap getBitmapFromCache(String url){
        String key = hashKeyForDisk(url);
        return lruCache.get(key);
    }
}
3.封装类已完成,那么接下来就是怎么使用了
由于测试的界面简单,就大概讲一下,就是在布局文件中创建一个ImageView和一个Button;

private String url = "http://www.xxx.xxx.png";
LruCacheUtils instance = LruCacheUtils.getInstance();//获取类
Button btn = (Button)findViewById(R.id.btn_test);
ImageView iv = (Button)findViewById(R.id.btn_test);
btn.setOnClickListener(new OnClickListener()){
    @override
    public void click(View v){
        //300,200为想要的图片尺寸
        loadBitmap(url,300,200);
    }
};

private void  loadBitmap(String url,int reqWidth,int reqHeight){
    //1、先从内存中获取
    Bitmap bitmap = instance.getBitmapFromCache();
    if(bitmap==null){
        //2、再从磁盘中获取
        InputStream is = instance.getDiskCache(url);
        if(is==null){
            //3、最后从网路中下载并保存内存中
            instance.fromNetToCache(url,,reqWidth,reqHeight,new LruCacheUtils.Callback() {
                @Override
                public void response(Bitmap entity) {
                    iv.setImageBitmap(entity);
                }
            });)
        }else{
            bitmap = BitmapFactory.decodeStream(is);
            //需要将磁盘中保存的文件添加到内存中
            instance.addBitmapToCache(url,bitmap);
            iv.setImageBitmap(bitmap);
        }
    }else{
        iv.setImageBitmap(bitmap);
    }
}

结束了,自己一定要研究一下帮助类中的方法啊。

相关TAG标签
上一篇:Github项目解析(十四)--)快速实现自定义地图聚合操作
下一篇:Swift使用CG和CI framework画棋盘
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站