博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
16.手写图片加载框架ImageLoader
阅读量:6879 次
发布时间:2019-06-26

本文共 28821 字,大约阅读时间需要 96 分钟。

概述

第三方开源的图片框架很多,这里自己去写一个的目的是通过这样一个写的过程,拓展自己对架构设计的理解,包括设计模式,线程,策略,缓存等等。另外大型的框架例如Glide,代码很完善,扩展性很高,但是阅读起来有难度,而实际上,这些框架底层实现原理都是类似的,所以通过构建一个简单框架的过程更加有助于对其原理的理解,算是为阅读复杂的第三方源码打下一个基础。

github地址:

今天的框架要实现一下的功能:

1.根据用户需求可以灵活配置(建造者模式)

2.支持高并发,图片加载的优先级
3.支持可以选择不同的加载策略,对加载策略进行扩展
4.二级缓存 加载图片时内存中已经加载了,则从内存中加载,不存在去外置卡中5.加载,外置还不存在则从网络下载
6.并对缓存策略可以扩展
7.支持从加载过程中显示默认加载图片
8.支持加载失败时 显示默认错误图片
9.图片显示自适应。从网络加载下来的图片经最佳比例压缩后显示不能失真变形
10.支持请求转发,下载

用到的模式:

1.生产者 消费者模式
2.建造者模式
3.单例模式
4.模板方法模式
5.策略模式

用到的知识点

1.内存缓存 LruCache技术
2.硬盘缓存技术DiskLruCache技术
3.图片下载时请求转发

img_f1fc6c053236a29dbfbe643a3dc00c74.png
ImageLoader类图.png
框架构建流程

如上图,首先使用Builder设计模式构建ImageLoaderConfig,这个类处理图片加载框架的全局配置信息,包括加载策略,缓存策略,线程数,以及加载中一些图片的配置,封装成了DisplayConfig对象;SimpleImageLoader是对外暴露的主类,它持有配置对象的引用,所以它可以调用所以图片加载中所涉及的配置,然后将这些配置封装成BitmapRequest对象,一个BitmapRequest对象对应一次网络请求,在SimpleImageLoader初始化的同时还会初始化全局的请求队列RequestQueue,BitmapRequest对象会被加入队列中,这时候分发器开始工作,将BitmapRequest按照一定协议分发给不同的加载器,加载器拿到请求后先从缓存BitmapCache中获取,有缓存则直接展示然后再去加载网络图片,加载网络图片的时候又涉及到加载策略的选择,根据不同策略进行不同的加载。

关键代码
1.配置管理

ImageLoaderConfig ,配置管理类,我们把一些关键的配置信息单独封装起来,以Builder模式构建,用户可以自由的配置自己需要的,同时提高扩展性,这正是Builder设计模式的优点.ImageLoaderConfig 负责处理整个框架的配置信息,目前包括缓存策略,加载策略,加载中显示的占位图,加载失败展示的占位图等等

package com.rzm.imageloader.config;import com.rzm.imageloader.cache.BitmapCache;import com.rzm.imageloader.policy.LoadPolicy;/** * Author:renzhenming * Time:2018/6/13 7:21 * Description:This is ImageLoaderConfig * 以建造者模式实现,管理ImageLoader配置信息 */public class ImageLoaderConfig {    /**     * 图片显示配置 TODO 初始化     */    private DisplayConfig displayConfig;    /**     * 缓存策略     */    private BitmapCache bitmapCache;    /**     * 加载策略     */    private LoadPolicy loadPolicy;    /**     * 默认线程数     */    private int threadCount = Runtime.getRuntime().availableProcessors();    private ImageLoaderConfig(){}    /**     * 建造者模式     */    public static class Builder{        /**         * Builder持有外部类的引用,在new的时候创建出来         */        private ImageLoaderConfig config;        public Builder(){            config = new ImageLoaderConfig();        }        /**         * 设置缓存策略         * @param bitmapCache         * @return         */        public Builder setCachePolicy(BitmapCache bitmapCache){            config.bitmapCache = bitmapCache;            return this;        }        /**         * 设置加载策略         * @param loadPolicy         * @return         */        public Builder setLoadPolicy(LoadPolicy loadPolicy){            config.loadPolicy = loadPolicy;            return this;        }        /**         * 设置线程数         * @param threadCount         * @return         */        public Builder setThreadCount(int threadCount){            config.threadCount = threadCount;            return this;        }        /**         * 设置加载过程中的图片         * @param resId         * @return         */        public Builder setLoadingImage(int resId){            if (config.displayConfig == null){                throw new NullPointerException("you have not set DisplayConfig,DisplayConfig is null");            }            config.displayConfig.loadingImage = resId;            return this;        }        /**         * 设置加载失败显示的图片         * @param resId         * @return         */        public Builder setErrorImage(int resId){            if (config.displayConfig == null){                throw new NullPointerException("you have not set DisplayConfig,DisplayConfig is null");            }            config.displayConfig.errorImage = resId;            return this;        }        /**         * 构建         * @return         */        public ImageLoaderConfig build(){            return config;        }    }    public DisplayConfig getDisplayConfig() {        return displayConfig;    }    public BitmapCache getBitmapCache() {        return bitmapCache;    }    public LoadPolicy getLoadPolicy() {        return loadPolicy;    }    public int getThreadCount() {        return threadCount;    }}
package com.rzm.commonlibrary.general.imageloader.config;/** * Author:renzhenming * Time:2018/6/13 7:24 * Description:This is DisplayConfig * 图片显示配置类,单独拿出来作为一个单独类有利于扩展,仿Glide */public class DisplayConfig {    /**     * 加载过程中的占位图片     */    public int loadingImage = -1;    /**     * 加载失败显示的图片     */    public int errorImage = -1;}
2.SimpleImageLoader

以单例形式构建的交互类,持有ImageLoaderConfig 配置引用,负责将每一个网络请求封装成BitmapRequest对象,SimpleImageLoader初始化的时候会构建出一个全局的阻塞式队列,BitmapRequest会被加入这个队列中,此时分发器开始工作,分发器的构建启发于Android消息队列中的looper,负责将每个Request请求从队列中取出交给负责处理这个请求的加载器

package com.rzm.commonlibrary.general.imageloader.loader;import android.graphics.Bitmap;import android.widget.ImageView;import com.rzm.commonlibrary.general.imageloader.config.DisplayConfig;import com.rzm.commonlibrary.general.imageloader.config.ImageLoaderConfig;import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;import com.rzm.commonlibrary.general.imageloader.request.RequestQueue;/** * Author:renzhenming * Time:2018/6/13 7:22 * Description:This is SimpleImageLoader */public class SimpleImageLoader {    /**     * 持有配置信息对象的引用     */    private ImageLoaderConfig config;    private static volatile SimpleImageLoader instance;    /**     * 请求队列     */    private RequestQueue requestQueue;    private SimpleImageLoader(){}    private SimpleImageLoader(ImageLoaderConfig config){        this.config = config;        //初始化请求队列        requestQueue = new RequestQueue(config.getDispatcherCount());        //开启请求队列        requestQueue.start();    }    /**     * 用于在Application中初始化ImageLoader 设置配置信息     * 必须,否则调用空参getInstance()会抛异常     * @param config     * @return     */    public static SimpleImageLoader init(ImageLoaderConfig config){        if (instance == null){            synchronized (SimpleImageLoader.class){                if (instance == null){                    instance = new SimpleImageLoader(config);                }            }        }        return instance;    }    /**     * 用于在代码中获取单例对象     * @return     */    public static SimpleImageLoader getInstance(){        if (instance == null){            throw new UnsupportedOperationException("SimpleImageLoader haven't been init with ImageLoaderConfig,call init(ImageLoaderConfig config) in your application");        }        return instance;    }    /**     * 显示图片     * @param imageView     * @param url     */    public void display(ImageView imageView,String url){        display(imageView,url,null,null);    }    /**     * 显示图片     * @param imageView     * @param url     */    public void display(ImageView imageView,String url,DisplayConfig displayConfig){        display(imageView,url,displayConfig,null);    }    /**     * 显示图片     * @param imageView     * @param url     */    public void display(ImageView imageView,String url,ImageListener listener){        display(imageView,url,null,listener);    }    /**     * 显示图片,用于针对特殊图片配置特殊的配置信息     * @param imageView     * @param url     * @param displayConfig     * @param listener     */    public void display(ImageView imageView,String url,DisplayConfig displayConfig,ImageListener listener){        if (imageView  == null){            throw new NullPointerException("ImageView cannot be null");        }        //封装成一个请求对象        BitmapRequest request= new BitmapRequest(imageView,url,displayConfig,listener);        //加入请求队列        requestQueue.addRequest(request);    }    /**     * 监听图片,设置后期处理,仿Glide     */    public static interface ImageListener{        void onComplete(ImageView imageView, Bitmap bitmap,String url);    }    /**     * 获取全局配置     * @return     */    public ImageLoaderConfig getConfig() {        return config;    }}
3.BitmapRequest请求对象

一个BitmapRequest对象中封装了这次请求的所有相关信息,包括这个请求的图片显示逻辑的配置DisplayConfig,当前全局的加载策略LoadPolicy,请求图片的网络地址和这个地址需要展示的控件对象ImageView,另外我对BitmapRequest对象重写了hashCode和equals方法,实现了Comparable接口,以实现这个对象的比较逻辑,为加载策略服务

package com.rzm.commonlibrary.general.imageloader.request;import android.widget.ImageView;import com.rzm.commonlibrary.general.imageloader.config.DisplayConfig;import com.rzm.commonlibrary.general.imageloader.loader.SimpleImageLoader;import com.rzm.commonlibrary.general.imageloader.policy.LoadPolicy;import com.rzm.commonlibrary.general.imageloader.utils.Md5Util;import java.lang.ref.SoftReference;/** * Author:renzhenming * Time:2018/6/13 7:23 * Description:This is BitmapRequest */public class BitmapRequest implements Comparable
{ /** * 展示配置 */ private DisplayConfig disPlayConfig; /** * 加载策略 */ private LoadPolicy loadPolicy = SimpleImageLoader.getInstance().getConfig().getLoadPolicy(); /** * 序列号,用于顺序比较 */ private int serialNum; /** * 持有ImageView的软引用 */ private SoftReference
imageViewSoftReference; /** * 图片路径 */ private String imageUrl; /** * 图片路径的md5值 */ private String imageUrlMd5; /** * 下载完成的监听 */ private SimpleImageLoader.ImageListener imageListener; public BitmapRequest() { } public BitmapRequest(ImageView imageView, String imageUrl) { this(imageView,imageUrl,null,null); } public BitmapRequest(ImageView imageView,String imageUrl,DisplayConfig displayConfig) { this(imageView,imageUrl,displayConfig,null); } public BitmapRequest(ImageView imageView,String imageUrl,SimpleImageLoader.ImageListener imageListener) { this(imageView,imageUrl,null,imageListener); } public BitmapRequest(ImageView imageView, String imageUrl, DisplayConfig displayConfig, SimpleImageLoader.ImageListener imageListener){ this.imageViewSoftReference = new SoftReference
(imageView); if (imageUrl != null) { imageView.setTag(imageUrl); imageUrlMd5 = Md5Util.toMD5(imageUrl); } this.imageUrl = imageUrl; if (displayConfig != null){ this.disPlayConfig = displayConfig; } if (imageListener != null) { this.imageListener = imageListener; } } /** * 请求的先后顺序是根据加载的策略进行的,不同的策略比较的条件也不同,所以 * 这里要把比较的逻辑交割具体的策略去做,策略有多种,所以通过接口调用,增强扩展性 * @return */ @Override public int compareTo(BitmapRequest o) { return loadPolicy.compareTo(o,this); } public int getSerialNum() { return serialNum; } public void setSerialNum(int serialNum) { this.serialNum = serialNum; } /** * 获取这个请求对应的ImageView * @return */ public ImageView getImageView(){ if (imageViewSoftReference == null) return null; return imageViewSoftReference.get(); } public DisplayConfig getDisPlayConfig() { return disPlayConfig; } public LoadPolicy getLoadPolicy() { return loadPolicy; } public SoftReference
getImageViewSoftReference() { return imageViewSoftReference; } public String getImageUrl() { return imageUrl; } public String getImageUrlMd5() { return imageUrlMd5; } public SimpleImageLoader.ImageListener getImageListener() { return imageListener; } /** * BitmapRequest会被加入请求队列中,在队列中有需要做判断当前请求是否存在 * 那么就涉及到这个对象的比较,所以需要重写hashCode和equals方法 */ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BitmapRequest request = (BitmapRequest) o; return serialNum == request.serialNum && loadPolicy.equals(request.loadPolicy); } @Override public int hashCode() { int result = loadPolicy != null ? loadPolicy.hashCode():0; result = 66*result+serialNum; return result; }}
4.分发器实现原理

RequestDispatcher是一个继承了Thread的线程对象,队列创建后会根据配置创建出指定数量的分发器,当队列中有请求对象后就从队列中取出对象交给加载器,根据一定的协议选择合适的加载器进行网络请求,当队列中没有对象时,会进入休眠状态

package com.rzm.commonlibrary.general.imageloader.request;import android.text.TextUtils;import android.util.Log;import com.rzm.commonlibrary.general.imageloader.loader.Loader;import com.rzm.commonlibrary.general.imageloader.loader.LoaderManager;import java.util.concurrent.BlockingQueue;/** * Author:renzhenming * Time:2018/6/13 7:24 * Description:This is RequestDispatcher */public class RequestDispatcher extends Thread{    /**     * 从队列中转发请求需要持有队列的引用     */    private BlockingQueue
blockingQueue; public RequestDispatcher(BlockingQueue
blockingQueue) { this.blockingQueue = blockingQueue; } /** * 阻塞式队列,转发器开启,从队列中取请求队列,如果没有则会阻塞当前线程,所以这里 * 是在子线程开启的 */ @Override public void run() { while(!isInterrupted()){ try { BitmapRequest request = blockingQueue.take(); //处理请求对象,交给loader String schema = parseSchema(request.getImageUrl()); //获取加载器 Loader loader = LoaderManager.getInstance().getLoader(schema); if (loader == null){ Log.d("TAG",request.getImageUrl() + "没有找到对应的加载器"); return; } loader.load(request); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 根据图片url判断加载类型 * @param imageUrl * @return */ private String parseSchema(String imageUrl) { if (TextUtils.isEmpty(imageUrl)){ return null; } if (imageUrl.contains("://")){ //形如 http://xxx 或者file://xxx,这样截取后 //可以获得http file等前缀,根据这个前缀获取相应 //的加载器 return imageUrl.split("://")[0]; }else{ Log.d("TAG","不持支的图片类型"); } return null; }}
5.加载器实现原理

目前该框架支持网络图片和本地图片加载,不同的加载器根据不同的url进行选择,为了提高扩展性,设置接口和抽象类的实现方式,下面是顶层接口

package com.rzm.commonlibrary.general.imageloader.loader;import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;/** * Author:renzhenming * Time:2018/6/13 7:21 * Description:This is Loader */public interface Loader {    /**     * 加载图片     * @param request     */    void load(BitmapRequest request);}

网络加载器和本地加载器实现逻辑有所不同,但是有一些公共的操作存在,比如加载前显示加载中占位图,加载失败显示失败图片等等,这些操作可以放在一个公共的基类中实现,所以这里创建了一个抽象类作为上层类处理公共逻辑

package com.rzm.commonlibrary.general.imageloader.loader;import android.graphics.Bitmap;import android.widget.ImageView;import com.rzm.commonlibrary.general.imageloader.cache.BitmapCache;import com.rzm.commonlibrary.general.imageloader.config.DisplayConfig;import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;import java.util.concurrent.atomic.AtomicInteger;/** * Author:renzhenming * Time:2018/6/13 7:22 * Description:This is AbstractLoader * 加载器策略不同,则不同的加载器实现方式不同,但是他们有相同的操作,比如显示 * 占位图等,所以这些相同操作在抽象一层出来 */public abstract class AbstractLoader implements Loader {    private static final String TAG = "AbstractLoader";    private AtomicInteger integer = new AtomicInteger(0);    /**     * 加载器加载图片的逻辑是先缓存后网络,所以需要持有缓存对象的引用     */    private BitmapCache bitmapCache = SimpleImageLoader.getInstance().getConfig().getBitmapCache();    /**     * 同样因为要处理显示时的逻辑,所以需要持有显示配置对象的引用     */    private DisplayConfig displayConfig = SimpleImageLoader.getInstance().getConfig().getDisplayConfig();    @Override    public void load(BitmapRequest request) {        //从缓存中获取Bitmap        Bitmap bitmap= null;        if (bitmapCache != null) {            bitmap = bitmapCache.get(request);        }        if (bitmap == null){            //显示加载中图片            showLoadingImg(request);            //开始加载网络图,加载的逻辑不同加载器有所不同,所以交给各自            //加载器实现,抽象            bitmap = onLoad(request);            if (bitmap == null){                //加载失败重试三次                while(integer.incrementAndGet() <=3){                    bitmap = onLoad(request);                    if (bitmap != null){                        break;                    }                }                integer.set(0);            }            if (bitmap == null){            }            //加入缓存            if (bitmapCache != null && bitmap != null)                cacheBitmap(request,bitmap);        }else{            //有缓存        }        deliveryToUIThread(request,bitmap);    }    public abstract Bitmap onLoad(BitmapRequest request);    protected void deliveryToUIThread(final BitmapRequest request, final Bitmap bitmap) {        ImageView imageView = request.getImageView();        if(imageView!=null) {            imageView.post(new Runnable() {                @Override                public void run() {                    updateImageView(request, bitmap);                }            });        }    }    private void updateImageView(final BitmapRequest request, final Bitmap bitmap) {        ImageView imageView = request.getImageView();        //加载正常  防止图片错位        if(bitmap != null && imageView.getTag().equals(request.getImageUrl())){            imageView.setImageBitmap(bitmap);        }        //有可能加载失败        if(bitmap == null && displayConfig!=null&&displayConfig.errorImage!=-1){            imageView.setImageResource(displayConfig.errorImage);        }        //监听        //回调 给圆角图片  特殊图片进行扩展        if(request.getImageListener() != null){            request.getImageListener().onComplete(imageView, bitmap, request.getImageUrl());        }    }    /**     * 缓存图片     * @param request     * @param bitmap     */    private void cacheBitmap(BitmapRequest request, Bitmap bitmap) {        if (request != null && bitmap != null){            synchronized (AbstractLoader.class){                bitmapCache.put(request,bitmap);            }        }    }    /**     * 显示加载中占位图,需要判断用户有没有配置     * @param request     */    private void showLoadingImg(BitmapRequest request) {        if (hasLoadingPlaceHolder()){            final ImageView imageView = request.getImageView();            if (imageView != null){                imageView.post(new Runnable() {                    @Override                    public void run() {                        imageView.setImageResource(displayConfig.loadingImage);                    }                });            }        }    }    /**     * 是否设置了加载中图片     * @return     */    private boolean hasLoadingPlaceHolder() {        return displayConfig != null && displayConfig.loadingImage > 0;    }}

接下来是具体的加载器实现

package com.rzm.commonlibrary.general.imageloader.loader;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.text.TextUtils;import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;import com.rzm.commonlibrary.general.imageloader.utils.BitmapDecoder;import com.rzm.commonlibrary.general.imageloader.utils.ImageViewHelper;import java.io.BufferedInputStream;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;/** * Author:renzhenming * Time:2018/6/13 7:22 * Description:This is UrlLoader * 网络图片加载器 */public class UrlLoader extends AbstractLoader {    @Override    public Bitmap onLoad(BitmapRequest request) {        try {            String imageUrl = request.getImageUrl();            if (TextUtils.isEmpty(imageUrl)){                return null;            }            URL url = new URL(imageUrl);            HttpURLConnection conn = (HttpURLConnection) url.openConnection();            if (conn.getResponseCode() != 200){                return null;            }            InputStream inputStream = conn.getInputStream();            //转化成BufferedInputStream            final BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);            //标记一下,reset后会重置到这个位置            bufferedInputStream.mark(inputStream.available());            BitmapDecoder decoder = new BitmapDecoder() {                @Override                public Bitmap decodeBitmapWithOptions(BitmapFactory.Options options) {                    //第一次读取,因为设置了inJustDecodeBounds为true,所以,这里decodeStream之后,会将宽高                    //信息存储在options中;第二次读取,因为设置了inJustDecodeBounds为false.所以会将流全部读取                    Bitmap bitmap = BitmapFactory.decodeStream(bufferedInputStream,null,options);                   if (options.inJustDecodeBounds){                       //表示时第一次执行,此时只是为了获取Bounds                       try {                           //第一次读取图片宽高信息,读完之后,要为第二次读取做准备,将流重置                           bufferedInputStream.reset();                       } catch (IOException e) {                           e.printStackTrace();                       }                   }else{                       try {                           bufferedInputStream.close();                       } catch (IOException e) {                           e.printStackTrace();                       }                   }                   return bitmap;                }            };            //传入控件的宽高,设置图片适应控件            return decoder.decodeBitmap(ImageViewHelper.getImageViewWidth(request.getImageView()),                    ImageViewHelper.getImageViewHeight(request.getImageView()));        } catch (MalformedURLException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        return null;    }}
package com.rzm.commonlibrary.general.imageloader.loader;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.net.Uri;import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;import com.rzm.commonlibrary.general.imageloader.utils.BitmapDecoder;import com.rzm.commonlibrary.general.imageloader.utils.ImageViewHelper;import java.io.File;/** * Author:renzhenming * Time:2018/6/13 7:22 * Description:This is LocalLoader * 本地图片加载器 */public class LocalLoader extends AbstractLoader {    @Override    public Bitmap onLoad(BitmapRequest request) {        //得到本地图片的路径        final String path = Uri.parse(request.getImageUrl()).getPath();        File file = new File(path);        if (!file.exists() || !file.isFile()){            return null;        }        BitmapDecoder decoder = new BitmapDecoder() {            @Override            public Bitmap decodeBitmapWithOptions(BitmapFactory.Options options) {                return BitmapFactory.decodeFile(path,options);            }        };        return decoder.decodeBitmap(ImageViewHelper.getImageViewWidth(request.getImageView()),                ImageViewHelper.getImageViewHeight(request.getImageView()));    }}
package com.rzm.commonlibrary.general.imageloader.utils;import android.graphics.Bitmap;import android.graphics.BitmapFactory;/** * 图片解码器 */public abstract class BitmapDecoder {    /**     * 压缩图片     * @param width imageView的宽度     * @param height imageView的高度     * @return     */    public Bitmap decodeBitmap(int width,int height){        BitmapFactory.Options options = new BitmapFactory.Options();        //设置为true 只读取图片的宽高,不需要将整个图片都加载到内存        options.inJustDecodeBounds = true;        decodeBitmapWithOptions(options);        //经过上面一次操作,此时options中已经有了宽高信息        calculateSampleSizeWithOptions(options,width,height);        //第二次就可以得到缩放后的bitmap了        return decodeBitmapWithOptions(options);    }    /**     * 将图片宽高和控件宽高进行比较,得到缩放值,信息仍然存储在options中     * @param options     * @param viewWidth     * @param viewHeight     */    private void calculateSampleSizeWithOptions(BitmapFactory.Options options,int viewWidth,int viewHeight) {        //计算缩放比例        //图片的原始宽高        int width = options.outWidth;        int height = options.outHeight;        int inSampleSize = 1;        //当图片的宽高大于控件的宽高时才需要压缩        if (width > viewWidth || height > viewHeight){            //计算出宽高的缩放比例            int widthRatio = Math.round((float) width/(float)viewWidth);            int heightRatio = Math.round((float)height/(float)viewHeight);            //取宽高缩放比较大的值为图片的缩放比            inSampleSize = Math.max(widthRatio,heightRatio);        }        //设置到options中,options保存的是配置信息        //当inSampleSize为2,图片的宽高会缩放为原来的1/2        options.inSampleSize = inSampleSize;        //每个像素2个字节        options.inPreferredConfig = Bitmap.Config.RGB_565;        //宽高已经计算出来了,inJustDecodeBounds值可以复位了        options.inJustDecodeBounds = false;        //当系统内存不足时.可以回收bitmap        options.inPurgeable = true;        options.inInputShareable = true;    }    /**     * 将流的处理通过抽象方法暴露出来,降低解码器和外部的耦合     * @param options     */    public abstract Bitmap decodeBitmapWithOptions(BitmapFactory.Options options);}
6.本地缓存
package com.rzm.commonlibrary.general.imageloader.cache;import android.graphics.Bitmap;import android.util.LruCache;import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;/** * Author:renzhenming * Time:2018/6/13 7:20 * Description:This is MemoryCache */public class MemoryCache implements BitmapCache{    private LruCache
mLruCache; public MemoryCache(){ int maxSize = (int) (Runtime.getRuntime().maxMemory()/1024/8); mLruCache = new LruCache
(maxSize){ @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes()*value.getHeight(); } }; } @Override public Bitmap get(BitmapRequest request) { if (mLruCache == null) return null; return mLruCache.get(request.getImageUrlMd5()); } @Override public void put(BitmapRequest request, Bitmap bitmap) { if (mLruCache == null) return; mLruCache.put(request.getImageUrlMd5(),bitmap); } @Override public void remove(BitmapRequest request) { if (mLruCache == null) return; mLruCache.remove(request.getImageUrlMd5()); }}
7.硬盘缓存
package com.rzm.commonlibrary.general.imageloader.disk;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import com.rzm.commonlibrary.general.imageloader.cache.BitmapCache;import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;import com.rzm.commonlibrary.general.imageloader.utils.IOUtil;import java.io.BufferedOutputStream;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;/** * Author:renzhenming * Time:2018/6/13 7:20 * Description:This is DiskCache */public class DiskCache implements BitmapCache {    private static volatile DiskCache mDiskCache;    //缓存路径    private String mCacheDir = "Image";    //MB    private static final int MB = 1024 * 1024;    //jackwharton的杰作    private DiskLruCache mDiskLruCache;    private DiskCache(Context context)    {        iniDiskCache(context);    }    public static DiskCache getInstance(Context context) {        if(mDiskCache==null)        {            synchronized (DiskCache.class)            {                if(mDiskCache==null)                {                    mDiskCache=new DiskCache(context);                }            }        }        return mDiskCache;    }    private void iniDiskCache(Context context) {        //得到缓存的目录  android/data/data/com.xxx/cache/Image        File directory=getDiskCache(mCacheDir,context);        if(!directory.exists())        {            directory.mkdirs();        }        try {            //最后一个参数 指定缓存容量            mDiskLruCache=DiskLruCache.open(directory,1,1,50*MB);        } catch (IOException e) {            e.printStackTrace();        }    }    private File getDiskCache(String mCacheDir, Context context) {        //默认缓存路径        return new File(context.getCacheDir(),mCacheDir);        //return new File(Environment.getExternalStorageDirectory(),mCacheDir);    }    @Override    public void put(BitmapRequest request, Bitmap bitmap) {        if (mDiskLruCache == null) return;        DiskLruCache.Editor edtor=null;        OutputStream os=null;        try {            //路径必须是合法字符            edtor=mDiskLruCache.edit(request.getImageUrlMd5());            os=edtor.newOutputStream(0);            if(persistBitmap2Disk(bitmap,os))            {                edtor.commit();            }else {                edtor.abort();            }        } catch (IOException e) {            e.printStackTrace();        }    }    private boolean persistBitmap2Disk(Bitmap bitmap, OutputStream os) {        BufferedOutputStream bos=new BufferedOutputStream(os);        bitmap.compress(Bitmap.CompressFormat.JPEG,100,bos);        try {            bos.flush();        } catch (IOException e) {            e.printStackTrace();        }finally {            IOUtil.closeQuietly(bos);        }        return true;    }    @Override    public Bitmap get(BitmapRequest request) {        if (mDiskLruCache == null) return null;        try {            DiskLruCache.Snapshot snapshot=mDiskLruCache.get(request.getImageUrlMd5());            if(snapshot!=null)            {                InputStream inputStream=snapshot.getInputStream(0);                return BitmapFactory.decodeStream(inputStream);            }        } catch (IOException e) {            e.printStackTrace();        }        return null;    }    @Override    public void remove(BitmapRequest request) {        if (mDiskLruCache == null) return;        try {            mDiskLruCache.remove(request.getImageUrlMd5());        } catch (IOException e) {            e.printStackTrace();        }    }}

转载地址:http://qvgfl.baihongyu.com/

你可能感兴趣的文章
关于【自证清白】
查看>>
手把手教你crontab排障
查看>>
订单编号
查看>>
纪念我曾经的 JAVA 姿势--转
查看>>
js 如何清除setinterval
查看>>
我为NET狂官方面试题-数据库篇答案
查看>>
玩转iOS开发:iOS开发中的装逼技术 - RunTime(一)
查看>>
CSS实现水平垂直居中的1010种方式(史上最全)
查看>>
BCH曼谷矿工会议的积极方面:社区彼此更加了解
查看>>
Android之观察者模式
查看>>
微信公众号支付开发全过程(Java 版)
查看>>
SwiftLint代码规范属性说明(二)
查看>>
本周半价(12.16-12.22)电子书
查看>>
是时候深入了解Linux的系统结构了
查看>>
4月第3周业务风控关注 | 文化部再次审查直播和游戏产品,已下架4939款直播应用...
查看>>
源码探探之startActivity(二)
查看>>
深入了解Flutter的isolate(1) ---- 事件循环(event loop)及代码运行顺序
查看>>
startService() 过程
查看>>
WebSocket 协议 1~4 节
查看>>
Android-WItemTouchHelperPlus几行代码搞定仿QQ侧滑
查看>>