Android图片二级缓存

本文介绍了一种图片双缓存机制,包括内存缓存和磁盘缓存的实现方式,通过这种方式可以有效提升图片加载效率并减少网络请求次数。

想起刚开始写代码的时候,领导叫我写一个头像下载的方法,当时屁颠屁颠就写了一个图片下载的,每次都要去网络上请求,最后直接被pass掉了

当时的思路是这样的


后来渐渐地就知道了有双缓存这东西。自己也阅读过很多关于双缓存的文章。

APP开发到越后面,对性能的要求越高,那么双缓存的优势就逐渐体现出来了。

所谓图片双缓存,首先到运行内存中请求,再到sd卡请求,最后到网络请求,流程图如下


那我们从第一部开始解析

1.先看 内存缓存的代码

public class MemoryCache implements ImageCache {
    private static final String TAG = MemoryCache.class.getSimpleName();
    private LruCache<String,Bitmap> mMemoryCache;
    public MemoryCache(){
        init();
    }

    private void init(){
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024);
        final int cacheSize = maxMemory/4;
        mMemoryCache = new LruCache<String,Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes()*value.getHeight()/1024;
            }
        };
    }
    @Override
    public Bitmap get(String key) {
        Bitmap bitmap = mMemoryCache.get(key);
        if (bitmap!=null){
            Log.i(TAG,"File is exist in memory");
        }
        return mMemoryCache.get(key);
    }

    @Override
    public void put(String key, Bitmap bitmap) {
        if (get(key)==null) {
            mMemoryCache.put(key, bitmap);
        }
    }
}


private void init()
init()方法中对一些变量进行初始化,mMemoryCache用于在内存中缓存图片

   public Bitmap get(String key) {}

get()方法用于从内存中获得缓存

  public void put(String key, Bitmap bitmap) {}
put()方法将下载好的图片缓存到内存中,方便下次使用


2.再看sd卡缓存

public class DiskCache implements ImageCache {
    private static final String TAG = DiskCache.class.getSimpleName();
    static String mPath ;
    public DiskCache(Context context){
        init(context);
    }
    private void init(Context context){
        // 获取图片缓存路径
        mPath = getDiskCachePath(context,"bitmap");
        File cacheDir = new File(mPath);
        if (!cacheDir.exists()) {
            cacheDir.mkdirs();
        }
    }
    @Override
    public Bitmap get(String key) {
        File file = new File(mPath+key);
        if (file.exists()){
            return BitmapFactory.decodeFile(mPath+key);
        }
        return null;
    }

    @Override
    public void put(String key, Bitmap bitmap) {
        FileOutputStream fileOutputStream = null;
        try {
            File file = new File(mPath+key);
            if (file.exists()){
                Log.i(TAG,"File is exist on disk");
            }
            fileOutputStream = new FileOutputStream(mPath+key);
            bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            CloseUtils.closeQuietly(fileOutputStream);
        }
    }

    /**
     * 根据传入的dir获得路径
     * @param context
     * @param dir
     * @return
     */
    public String getDiskCachePath(Context context, String dir) {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            cachePath = context.getCacheDir().getPath();
        }
        return cachePath + File.separator + dir;
    }

}
同样

private void init()
init()方法中对一些变量进行初始化,mMemoryCache用于在内存中缓存图片

   public Bitmap get(String key) {}

get()方法用于从内存中获得缓存

  public void put(String key, Bitmap bitmap) {}
put()方法将下载好的图片缓存到内存中,方便下次使用

接下来我们会在一个叫DoubleCache的类中对以上两种缓存方式进行管理

public class DoubleCache implements ImageCache {
    private static final String TAG = DoubleCache.class.getSimpleName();
    private MemoryCache mMemoryCache = null;
    private DiskCache mDiskCache = null;

    public DoubleCache(Context context){
        mMemoryCache = new MemoryCache();
        mDiskCache = new DiskCache(context);
    }
    @Override
    public Bitmap get(String url) {
        String key = url2Key(url);
        Bitmap bitmap = mMemoryCache.get(key);
        if(bitmap==null){
            bitmap = mDiskCache.get(key);
        }else {
        }
        return bitmap;
     }

    @Override
    public void put(String url, Bitmap bitmap) {
        String key = url2Key(url);
        mMemoryCache.put(key,bitmap);
        mDiskCache.put(key,bitmap);
    }

    //url转key
    private String url2Key(String url){
        String key = MD5.hashKeyForDisk(url)+".jpg";
        return key;
    }
}
我们在获取缓存的时候先从内存中获取,当内存中击中直接返回,当内存中没有击中,则访问sd卡。

3.看到这里,小伙伴们一定急了,这只有从缓存中和sd卡中取图片,并没有从网络获取,别急,马上就来

public class ImageLoader {
    private static final String TAG = ImageLoader.class.getSimpleName();
    
    private static ImageLoader sInstance;

    private DoubleCache mDoubleCache = null;

    private ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    private ImageLoader(Context context) {
        mDoubleCache = new DoubleCache(context);
    }

    public static ImageLoader getInstance(Context context) {
        if (sInstance == null) {
            synchronized (ImageLoader.class) {
                sInstance = new ImageLoader(context);
            }
        }
        return sInstance;
    }

    public void displayImage(String url, ImageView imageView) {
        Bitmap bitmap = mDoubleCache.get(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            mDoubleCache.put(url,bitmap);
            return;
        }
        submitLoadRequest(url, imageView);
    }

    private void submitLoadRequest(final String url, final ImageView imageView) {
        Log.i(TAG,"Download,url:"+url);
        imageView.setTag(url);
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                final Bitmap bitmap = downloadImage(url);
                if (imageView.getTag().equals(url)) {

                    imageView.post(new Runnable() {
                        @Override
                        public void run() {
                            imageView.setImageBitmap(bitmap);
                        }
                    });
                }
                mDoubleCache.put(url, bitmap);
            }
        });
    }
    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {

        }
    };

    public Bitmap downloadImage(String url) {
        Bitmap bitmap = null;
        HttpURLConnection conn = null;
        try {
            URL url1 = new URL(url);
            conn = (HttpURLConnection) url1.openConnection();
            bitmap = BitmapFactory.decodeStream(conn.getInputStream());
            if (bitmap!=null){
                mDoubleCache.put(url,bitmap);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
        return bitmap;
    }

displayImage()
方法中可以看到,如果缓存中都没有才从网络中获取

public Bitmap downloadImage(String url) {}
下载完成之后,把图片放到缓存中。

到这里,我们的第二张流程图就走完了。是不是很简单。

我们在看一下是如何使用的

    private ImageView imageView;
    private ImageView imageView2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.image);
        imageView2 = (ImageView) findViewById(R.id.image2);
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                ImageLoader.getInstance(MainActivity.this).displayImage("https://img-my.csdn.net/uploads/201407/26/1406383299_1976.jpg", imageView);
            }
        });
        imageView2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                ImageLoader.getInstance(MainActivity.this).displayImage("https://img-my.csdn.net/uploads/201407/26/1406383299_1976.jpg", imageView2);
            }
        });
    }



源码在这里



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值