Android 图片加载之三级缓存

介绍

从流程逻辑上来说,三级分为:

1.内存缓存,优先加载的缓存,读取速度最快

2.本地缓存,次优先加载的缓存,存在文件中,读取本地文件

3.网络缓存,最后才加载的缓存,不优先加载,需要联网,消耗流量,当本地缓存和内存都没有的情况下再加载,加载速度慢,网络访问后需下载到本地。

原理

首次加载图片时,要通过网络交互来获取图片,可用任意一种网络请求框架下载该图,之后我们可以将图片保存至本地SD卡和内存中

之后运行软件,优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中的图片,本地SD卡再存储到内存中,内存有的话直接读取该图片

只在初次访问新内容时,才通过网络获取图片资源,其他情况下取内存以及本地缓存,顺序的优先级为内存>本地存储>网络存储

实现代码例子(参考https://www.jianshu.com/p/2cd59a79ed4a)感谢作者wanbo_

1. 自定义的图片缓存工具类(MyBitmapUtils)

通过new MyBitmapUtils().display(ImageView ivPic, String url) 提供给外部方法进行图片缓存的接口

参数含义:ivPic 用于显示图片的ImageView,url 获取图片的网络地址

package ****************************************

import android.graphics.Bitmap;

import android.widget.ImageView;

import com.hanweb.android.platform.utils.BitmapUtils;

/**

* Created by fppon 2018/8/21.

*/

public class MyBitmapUtils {

private NetCacheUtils  mNetCacheUtils;

    private MemoryCacheUtils  mMemoryCacheUtils;

    private LocalCacheUtils  mLocalCacheUtils;

    public MyBitmapUtils(){

mMemoryCacheUtils=new MemoryCacheUtils();

        mLocalCacheUtils=new LocalCacheUtils();

        mNetCacheUtils=new NetCacheUtils(mLocalCacheUtils,mMemoryCacheUtils);

//        new MyBitmapUtils().disPlay(ivPic,url);

    }

public void disPlay(ImageView ivPic,String url){

Bitmap bitmap;

        bitmap=mMemoryCacheUtils.getBitmapFromMemory(url);

        if(bitmap!=null){

ivPic.setImageBitmap(bitmap);

return;

        }

bitmap=mLocalCacheUtils.getBitmapFromLocal(url);

        if(bitmap!=null){

ivPic.setImageBitmap(bitmap);

            mMemoryCacheUtils.setBitmapToMemory(url,bitmap);

return;

        }

mNetCacheUtils.getBitmapFronNet(ivPic,url);

    }

}

2. 网络缓存(NetCacheUtils)

网络缓存中主要用到了AsyncTask来进行异步数据的加载

简单来说,AsyncTask可以看作是一个对handler和线程池的封装,通常,AsyncTask主要用于数据简单时,handler+thread主要用于数据量多且复杂时

也可以使用线程池ExecutorService

同时,为了避免内存溢出的问题,我们可以在获取网络图片后。对其进行图片压缩

例子如下:包含线程池,注释掉的代码

/**

* Created by fpp on 2018/8/21.

* 三级缓存之网络缓存

*/

public class NetCacheUtils {

private MemoryCacheUtils  mMemoryCacheUtils;

    private LocalCacheUtils  mLocalCacheUtils;

    //线程池的方式下载

//    ExecutorService executorService;

//    Handler handler=new Handler(){

//        @Override

//        public void handleMessage(Message msg) {

//            super.handleMessage(msg);

//            //listener传输值

//        }

//    };

    public NetCacheUtils(LocalCacheUtils  localCacheUtils, MemoryCacheUtils memoryCacheUtils){

mMemoryCacheUtils=memoryCacheUtils;

        mLocalCacheUtils=localCacheUtils;

        //构造方法传一个listener进来

//        executorService= Executors.newFixedThreadPool(5);

//        executorService.execute(new Runnable() {

//            @Override

//            public void run() {

//                Message message=handler.obtainMessage();

//                Bitmap resultbitmap=downLoadBitmap("");

//                message.obj=resultbitmap;

//                //从网络获取后保存至本地缓存

//                mLocalCacheUtils.setBitmapToLocal("",resultbitmap);

//                //从网络获取后保存至内存中

//                mMemoryCacheUtils.setBitmapToMemory("",resultbitmap);

//                handler.sendMessage(message);

//            }

//        });

    }

//从网络下载图片

    public void getBitmapFronNet(ImageView ivPic,String url){

new BitmapTask().execute(ivPic,url);

    }

class BitmapTaskextends AsyncTask{

private ImageViewivPic;

        private Stringurl;

        @Override

        protected BitmapdoInBackground(Object... objects) {

ivPic=(ImageView) objects[0];

            url=(String)objects[1];

            return downLoadBitmap(url);

        }

@Override

        protected void onPreExecute() {

super.onPreExecute();

        }

@Override

        protected void onProgressUpdate(Void... values) {

super.onProgressUpdate(values);

        }

@Override

        protected void onPostExecute(Bitmap resultbitmap) {

if(resultbitmap!=null){

ivPic.setImageBitmap(resultbitmap);

                //从网络获取后保存至本地缓存

                mLocalCacheUtils.setBitmapToLocal(url,resultbitmap);

                //从网络获取后保存至内存中

                mMemoryCacheUtils.setBitmapToMemory(url,resultbitmap);

            }

}

}

private BitmapdownLoadBitmap(String url){

HttpURLConnection conn=null;

        try {

conn=(HttpURLConnection)new URL(url).openConnection();

            conn.setConnectTimeout(5000);

            conn.setReadTimeout(5000);

            conn.setRequestMethod("GET");

            int responseCode=conn.getResponseCode();

            if(responseCode==200){

//图片压缩

                BitmapFactory.Options options=new BitmapFactory.Options();

                options.inSampleSize=2;

                //压缩为原来的1/2

                options.inPreferredConfig= Bitmap.Config.ARGB_4444;

                Bitmap bitmap=BitmapFactory.decodeStream(conn.getInputStream(),null,options);

                return bitmap;

            }

}catch (IOException e) {

e.printStackTrace();

        }finally {

conn.disconnect();

        }

return null;

    }

/**

* 定义一个接口,里面有一个方法,

* 这里有一个Bitmap对象参数,作用是让调用这接收这个Bitmap对象,实际这bitmap对象就是缓存中的对象

*/

    public interface ImageLoadListener {

public void loadImage(Bitmap bmp);

    }

3. 本地缓存(LocalCacheUtils)

在初次通过网络获取图片后,我们可以在本地SD卡中将图片保存起来

可以使用MD5加密图片的网络地址,来作为图片的名称保存

/**

* Created by fpp on 2018/8/21.

* 三级缓存之本地缓存

*/

public class LocalCacheUtils {

private static final StringCACHE_PATH= Environment.getExternalStorageDirectory().getAbsolutePath()+"/WerbNews";

    /**

* 从本地读取图片

*/

    public BitmapgetBitmapFromLocal(String url){

String fileName=null;

        //把图片的url当做文件名,并进行MD5加密

        fileName= MD5.md5(url);

        File file=new File(CACHE_PATH,fileName);

        try {

Bitmap bitmap= BitmapFactory.decodeStream(new FileInputStream(file));

            return bitmap;

        }catch (FileNotFoundException e) {

e.printStackTrace();

        }

return null;

    }

/**

* 从网络获取图片后保存至本地缓存

*/

    public void setBitmapToLocal(String url,Bitmap bitmap){

String fileName=MD5.md5(url);

        //把图片的url当做文件名,并进行MD5加密

        File file=new File(CACHE_PATH,fileName);

        //通过得到文件的父文件,判断父文件是否存在

        File parentFile=file.getParentFile();

        if(!parentFile.exists()){

parentFile.mkdir();

        }

try {

bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(file));

        }catch (FileNotFoundException e) {

e.printStackTrace();

        }

}

}

4. 内存缓存(MemoryCacheUtils)

这是本文中最重要且需要重点介绍的部分

进行内存缓存,就一定要注意一个问题,那就是内存溢出(OutOfMemory)

为什么会造成内存溢出?

Android 虚拟机默认分配给每个App 16M的内存空间,真机会比16M大,但任会出现内存溢出的情况

Android 系统在加载图片时是解析每一个像素的信息,再把每一个像素全部保存至内存中

图片大小 = 图片的总像素 * 每个像素占用的大小

单色图:每个像素占用1/8个字节,16色图:每个像素占用1/2个字节,256色图:每个像素占用1个字节,24位图:每个像素占用3个字节(常见的rgb构成的图片)

例如一张1920x1080的JPG图片,在Android 系统中是以ARGB格式解析的,即一个像素需占用4个字节,图片的大小=1920x1080x4=7M

实现方法:

通过HashMap键值对的方式保存图片,key为地址,value为图片对象,但因是强引用对象,很容易造成内存溢出,可以尝试SoftReference软引用对象

通过HashMap>SoftReference 为软引用对象(GC垃圾回收会自动回收软引用对象),但在Android2.3+后,系统会优先考虑回收弱引用对象,官方提出使用LruCache

通过LruCacheleast recentlly use 最少最近使用算法

会将内存控制在一定的大小内, 超出最大值时会自动回收, 这个最大值开发者自己定

/**

* Created by fpp on 2018/8/21.

* 三级缓存之内存缓存

*/

public class MemoryCacheUtils {

//    private HashMap mMemoryCache=new HashMap<>();//1.因为强引用,容易造成内存溢出,所以考虑使用下面弱引用的方法

    private HashMap>mMemoryCache =new HashMap<>();//2.因为在Android2.3+后,系统会优先考虑回收弱引用对象,官方提出使用LruCache

//    private LruCache mMemoryCache;

    public MemoryCacheUtils(){

long maxMemory=Runtime.getRuntime().maxMemory()/8;

        //得到手机最大允许内存的1/8,超过指定内存进行回收

//        mMemoryCache=new LruCache((int)maxMemory){

//            @Override

//            protected int sizeOf(String key, Bitmap value) {

//                int byteCount=value.getByteCount();

//                return byteCount;

//            }

//        };

    }

//从内存中读取图片

    public BitmapgetBitmapFromMemory(String url){

//强引用方法

//        Bitmap bitmap=mMemoryCache.get(url);

//弱引用方法

        SoftReference bitmapSoftReference=mMemoryCache.get(url);

        if(bitmapSoftReference!=null){

Bitmap bitmap1=bitmapSoftReference.get();

            return  bitmap1;

        }

//        return bitmap;

        return null;

    }

//往内存中写图片

    public void setBitmapToMemory(String url,Bitmap bitmap){

//强引用

//        mMemoryCache.put(url,bitmap);

//弱引用

        mMemoryCache.put(url,new SoftReference<>(bitmap));

    }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,639评论 18 139
  • 一、简介 在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫Glide的图片加载库,作者是bumptech。这...
    天天大保建阅读 7,461评论 2 28
  • 这一生该何去何从? 我深深相信,那些我想要抵达的地方,都将藏着我灵魂丢失的碎片。 过往和未来的人生里,唯一的指路人...
    牧野清风阅读 621评论 0 3
  • 高挑的你倚靠在桌边 双臂交叠 姿态放松 刚刚洗过澡 脚上穿着拖鞋 头发湿漉漉的 青春的朝气动人心魄 从那以后 每当...
    李镇阅读 94评论 0 0
  • 看到别人写的“三分钟热度毁了多少人”,真是深有感触啊,呵呵… 我想我无疑算是被毁的那一个人。现如今离开学校已...
    梧桐树9阅读 292评论 3 3