android Imageloader实现图像的三级缓存和代码结构优化

三级缓存的概念:即,网络,本地,内存,在安卓中,加载网络资源(特别是图片)是一件很消耗资源的资源的,因此我们使用三级缓存的形式,可以大大减少APP资源的消耗,增加开发效率,下面是三级缓存的流程图

Created with Raphaël 2.1.0 Activity开始 APP内存 是否有图片缓存? ImagView显示 本地存储 yes no
Created with Raphaël 2.1.0 本地存储 SD卡存储 是否有图片缓存? ImagView显示 网络获取 yes no
Created with Raphaël 2.1.0 网络获取 HTTP请求 是否有网络图片返回? ImagView显示 没有网络图片,抛出异常 yes no

由流程图可以知道,三级缓存最先访问的是APP的内存,其次是本地缓存,最后才是直接从网络获取图片。下面我们开始将这三个部分一起来实现下
在开始之前呢我们先来设计下项目的架构和思路,项目的架构对以后的维护是相当重要的,我们定义几个类,每个类分别实现以下功能
ImagLoader类:该类负责管理ImageView 显示图像的逻辑处理(只要负责图像的显示)
ImageCache 接口:该接口用于定义图像缓存的方法
MemoryCache 类:该类实现了ImageCache 接口接口,用来将图像缓存到APP内存和将图像从APP内存中取出来
DiskCache 类:该类实现了ImageCache 接口接口,用来将图像缓存到本地缓存和将图像从本地缓存中取出来
DoubleCache 类:该类用来处理MemoryCache和DiskCache之间的逻辑关系,也就是先从APP内存获取,再从本地缓存获取的逻辑关系
1 首先我们先来定义ImageCache接口

public interface ImageCache  {

    public Bitmap get(String url);

    public void set(String url,Bitmap bitmap);
}

该类中定义了两个方法,get方法是从缓存里面(包括本地缓存和内存缓存)获取bitmap,set方法是把bitmap设置到缓存里面
2 其次,我们来实现MemoryCache类

public class MemoryCache implements ImageCache {

    private LruCache<String ,Bitmap>mMemoryCache;



    public MemoryCache(){
        final int max=(int) (Runtime.getRuntime().maxMemory()/1024);
        mMemoryCache=new LruCache<String,Bitmap>(max/4){
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes()*bitmap.getHeight()/1024;
            }
        };
    }
    @Override
    public Bitmap get(String url) {
        Bitmap bitmap=mMemoryCache.get(url);
        if (bitmap==null){
            Log.e("TAG","读取内存缓存失败");
        }else {
            Log.e("TAG","读取内存缓存成功");
        }
        return bitmap;
    }
    @Override
    public void set(String url, Bitmap bitmap) {
        mMemoryCache.put(url,bitmap);
    }
}

在该类中,我们使用了安卓提供的LruCache来实现内存缓存,通过Runtime.getRuntime().maxMemory()获取APP当前可用内存的大小来定义使用内存缓存的阀值,避免出现OOM这种导致APP崩溃的尴尬事件,值得注意的是,使用LruCache的时候一定要注意key的值是否正确,否则会出现返回值为空的现象。
关于LruCache的更多资料可用查看官网API介绍,本文不再做过多介绍
LruCache官方API地址
3 接下来我们来实现DiskCache类

public class DiskCache implements ImageCache {

//    private String filepath="sdcard/cache/";
     private String filepath= Environment.getExternalStorageDirectory().toString()+"/Imageloder/";
    @Override
    public Bitmap get(String url) {
        Bitmap bitmap=BitmapFactory.decodeFile(filepath+url);
        if (bitmap==null){
            Log.e("TAG","读取本地缓存失败");
            return null;
        }else{
            Log.e("TAG","读取本地缓存成功");
            return bitmap;
        }
    }
    @Override
    public void set(String url, Bitmap bitmap) {
        File appDir = new File(Environment.getExternalStorageDirectory(), "Imageloder");
        if (!appDir.exists()) {
            appDir.mkdir();
            Log.e("TAG","创建本地文件夹");
        }
        String fileName = url;
        File file = new File(appDir, fileName);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.flush();
            fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

该类需要使用到读取文件的权限,请在XML中注册,在Android6.0以上的设备需要使用动态权限设置,本文直接指定使用5.1的SDK

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>


4 在实现完本地缓存之后,我们开始来实现处理本地和内存的逻辑关系类
DoubleCache ,该类也是实现ImageCache接口,但是在get方法和put的方法中做了不同的处理:

public class DoubleCache implements ImageCache {
    private ImageCache mMemoryCache;
    private ImageCache mDiskCache;

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

    @Override
    public void set(String url, Bitmap bitmap) {
        mMemoryCache.set(url,bitmap);
        mDiskCache.set(url,bitmap);
    }
}

在该类的get方法中,首先我们先通过mMemoryCache.get(url)来获取内存中的bitmap对象,当内存中没有bitmap返回时,我们将从本地缓存中
(mDiskCache.get(url))获取bitmap对象,最后再返回bitmap对象
5 最后我们的在来实现ImagLoader

public class ImageLoader  {
    ImageCache imageCache=new MemoryCache();
    ExecutorService mExecutorService= Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    public void display(String url, ImageView imageView,String key){
        Bitmap bitmap=imageCache.get(key);
        if (bitmap!=null){
            imageView.setImageBitmap(bitmap);
            return;
        }
        submitLoadRequest(url,imageView,key);
    }

    public void setImageCache(ImageCache imageCache) {
        this.imageCache = imageCache;
    }

    private void submitLoadRequest(final String url, final ImageView imageView,final String key){
        imageView.setTag(url);
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap=downloadImag(url);
                if (bitmap==null){
                    Log.e("TAG","图片下载失败");
                    return;
                }else {
                    Log.e("TAG","图片下载成功");
                    imageCache.set(key,bitmap);
                    if (imageView.getTag().equals(url)){
                        imageView.setImageBitmap(bitmap);
                    }
                }
            }
        });
    }

    private Bitmap downloadImag(String url){
        Bitmap bitmap=null;
        try {
            URL url1=new URL(url);
            final HttpURLConnection connection=(HttpURLConnection) url1.openConnection();
            bitmap= BitmapFactory.decodeStream(connection.getInputStream());
            connection.disconnect();
        }catch (Exception e){
            e.printStackTrace();
        }
        return bitmap;
    }
}

在该类中,我们定义的一个公开的方法display给外部调用,display中有三个参数,url是指当前要显示的图片的网络url地址,Imagview是当前要显示图片的控件,key是指图片缓存到APP内存时的索引或者是图片缓存到本地的文件名称,可能有的人会问,名称和索引直接用url不行?为什么要多定义一个看起来没有用的key?在很多网络图片中,url是相当长的,而且可能会有一些字符是我们Android的文件系统无法正常识别的,这时候我们就不能直接用url做文件名,值得注意的是,在该类中,我们灵活定义了setImageCache方法,该方法的存在使得用户可以很方便的定义自己使用哪种缓存方式(注:在三级缓存里面不一定是三种缓存都要使用的,可以只使用本地,也可以只使用内存,也可以本地和内存混合使用)
6最后,我们在Activity中使用ImageLoader就可以了,本文的例子:

 String url="http://img1.gamedog.cn/2014/04/24/119-  1404240UG30.jpg";
 ImageLoader imageLoader;
 ImageCache imageCache;
 imageLoader=new ImageLoader();
 imageCache=new DoubleCache();
 imageLoader.setImageCache(imageCache);
 imageLoader.display(url,imageView,"01.jpg");

运行结果:
运行结果

本文源码地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值