美文网首页
Android 图片加载之三级缓存

Android 图片加载之三级缓存

作者: 睿尧 | 来源:发表于2018-08-21 14:57 被阅读0次

    介绍

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

    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));

        }

    }

    相关文章

      网友评论

          本文标题:Android 图片加载之三级缓存

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