• 移动架构-图片加载框架设计


    图片加载核心就那些东西,这里设计一个图片加载框架,涉及到本地加载和网络加载,内存缓存和硬盘缓存,等等

    思路

    在getView的时候开始框架的调用

    1. 配置一系列环境,包括加载策略,缓存策略,线程数量
    2. 调用图片显示,从而添加请求到执行队列
    3. 请求及转发请求,调用加载器,根据需要从本地或者网络得到图片
    4. 得到的图片再选择缓存策略,硬盘缓存或者内存缓存
    5. 最后将图片显示出来

    实现的功能和用到的知识

    根据用户需求可以灵活配置
    支持高并发,图片加载的优先级
    支持可以选择不同的加载策略,对加载策略进行扩展
    二级缓存:加载图片时内存中已经加载了,则从内存中加载,不存在去外置卡中加载,外置还不存在则从网络下载
    并对缓存策略可以扩展
    支持从加载过程中显示默认加载图片
    支持加载失败时,显示默认错误图片
    图片显示自适应,从网络加载下来的图片经最佳比例压缩后显示
    不能失真变形
    支持请求转发,下载

    用到的模式:
    生产者 消费者模式
    建造者模式
    单例模式
    模板方法模式
    策略模式

    用到的知识点
    内存缓存 LruCache技术
    硬盘缓存技术DiskLruCache技术
    图片下载时请求转发

    实现代码

    首先是配置类,DisplayConfig和ImageLoaderConfig,这两个类主要用于显示及下载的初始化配置

    //显示图片配置
    public class DisplayConfig {
        //默认显示的图片ID
        public int loadingImage = -1;
        public int failedImage = -1;
    }
    
    //图片下载配置
    public class ImageLoaderConfig {
        //缓存策略
        private BitmapCache bitmapCache = new MemoryCache();
        //加载策略
        private LoadPolicy loadPolicy = new ReversePolicy();
        //默认线程数
        private int threadCount = Runtime.getRuntime().availableProcessors();
        //加载过程显示图片
        private DisplayConfig displayConfig= new DisplayConfig();
        private ImageLoaderConfig() {}
    
        //建造者模式,使用链式建造
        public static class Builder {
            private ImageLoaderConfig config;
    
            public Builder() {
                config = new ImageLoaderConfig();
            }
    
            //设置缓存策略
            public Builder setCachePolicy(BitmapCache bitmapCache) {
                config.bitmapCache = bitmapCache;
                return this;
            }
    
            //设置加载策略
            public Builder setLoadPolicy(LoadPolicy loadPolicy) {
                config.loadPolicy = loadPolicy;
                return this;
            }
    
            //设置线程数量
            public Builder setThreadCount(int count) {
                config.threadCount = count;
                return this;
            }
    
            //设置加载过程中的图片
            public Builder setLoadingImage(int resID) {
                config.displayConfig.loadingImage = resID;
                return this;
            }
    
            //设置加载失败的图片
            public Builder setFaildImage(int resID){
                config.displayConfig.failedImage = resID;
                return this;
            }
    
            //返回配置
            public ImageLoaderConfig build() {
                return config;
            }
        }
    
        public BitmapCache getBitmapCache() {
            return bitmapCache;
        }
    
        public LoadPolicy getLoadPolicy() {
            return loadPolicy;
        }
    
        public int getThreadCount() {
            return threadCount;
        }
    
        public DisplayConfig getDisplayConfig() {
            return displayConfig;
        }
    }
    

    然后是图片的请求类,包括BitmapRequest,RequestDispatcher和RequestQueue,用于完成Bitmap请求的封装,请求的转发及请求队列的管理

    //bitmap请求
    public class BitmapRequest implements Comparable<BitmapRequest> {
        //加载策略
        private LoadPolicy loadPolicy = SimpeImageLoader.getInstance().getConfig().getLoadPolicy();
        //编号
        private int serialNo;
        //持有ImageView的软引用
        private SoftReference<ImageView> imageViewSoft;
        //图片路径
        private String imageUrl;
        //MD5图片路径
        private String imageUrlMD5;
        //下载完成监听
        public SimpeImageLoader.ImageListener imageListener;
        //设置显示配置
        private DisplayConfig displayConfig;
    
        public BitmapRequest(ImageView imageView, String imageUrl, DisplayConfig displayConfig,
                             SimpeImageLoader.ImageListener imageListener) {
            this.imageViewSoft = new SoftReference<>(imageView);
            //设置可见Image的Tag,防止图片错位
            imageView.setTag(imageUrl);
            this.imageUrl = imageUrl;
            this.imageUrlMD5 = MD5Utils.toMD5(imageUrl);
            if (displayConfig != null) {
                this.displayConfig = displayConfig;
            }
            this.imageListener = imageListener;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            BitmapRequest that = (BitmapRequest) o;
            return serialNo == that.serialNo &&
                    Objects.equals(loadPolicy, that.loadPolicy);
        }
    
        @Override
        public int hashCode() {
    
            return Objects.hash(loadPolicy, serialNo);
        }
    
        public int getSerialNo() {
            return serialNo;
        }
    
        public void setSerialNo(int serialNo) {
            this.serialNo = serialNo;
        }
    
        public ImageView getImageView() {
            return imageViewSoft.get();
        }
    
        public String getImageUrl() {
            return imageUrl;
        }
    
        public String getImageUrlMD5() {
            return imageUrlMD5;
        }
    
        public DisplayConfig getDisplayConfig() {
            return displayConfig;
        }
    
        //间接比较,确定优先级
        @Override
        public int compareTo(@NonNull BitmapRequest o) {
            return loadPolicy.compareto(o, this);
        }
    }
    
    //转发器,请求转发线程,从请求队列中获取请求
    public class RequestDispatcher extends Thread{
        private static final String TAG = "RequestDispatcher";
        //请求队列
        private BlockingQueue<BitmapRequest> requests;
    
        public RequestDispatcher(BlockingQueue<BitmapRequest> requests) {
            this.requests = requests;
        }
    
        @Override
        public void run() {
            while(!isInterrupted()){
                try {
                    BitmapRequest request = requests.take();
                    //处理请求对象
                    //解析请求头
                    String schema = pareSchema(request.getImageUrl());
                    //获取加载器
                    Loader loader = LoaderManager.getInstance().getLoader(schema);
                    //加载图片
                    loader.loadImage(request);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        //判断图片路劲来源
        private String pareSchema(String imageUrl) {
            if(imageUrl.contains("://")){
                return imageUrl.split("://")[0];
            }else {
                Log.d(TAG, "不支持此文件类型");
            }
            return null;
        }
    }
    
    //请求队列
    public class RequestQueue {
        private static final String TAG = "RequestQueue";
        //阻塞式队列,多线程共享
        private BlockingQueue<BitmapRequest> requests = new PriorityBlockingQueue<>();
        //转发器数量
        private int threadCount;
        //一组转发器
        private RequestDispatcher[] dispatchers;
        //请求编号
        private AtomicInteger count = new AtomicInteger(0);
    
        public RequestQueue(int threadCount) {
            this.threadCount = threadCount;
        }
    
        //添加请求对象
        public void addRequest(BitmapRequest request) {
            if (!requests.contains(request)) {
                //给请求进行编号
                request.setSerialNo(count.incrementAndGet());
                requests.add(request);
            } else {
                Log.d(TAG, "请求已存在:" + request.getSerialNo());
            }
        }
    
        //开始请求
        public void start() {
            stop(); //开始前要先停止
            starDispatchers();
        }
    
        private void starDispatchers() {
            dispatchers = new RequestDispatcher[threadCount];
            for (int i = 0; i < threadCount; i++) {
                RequestDispatcher dispatcher = new RequestDispatcher(requests);
                dispatchers[i] = dispatcher;
                dispatchers[i].start();
            }
        }
    
        //停止请求
        public void stop() {
    
        }
    }
    

    缓存策略类包括缓存接口BitmapCache,硬盘缓存DiskCache,内存缓存MemoryCache和双缓存DoubleCache

    //缓存策略接口
    public interface BitmapCache {
        //缓存Bitmap
        void put(BitmapRequest request, Bitmap bitmap);
        //获取Bitmap
        Bitmap get(BitmapRequest request);
        //移除缓存
        void remove(BitmapRequest request);
    }
    
    //硬盘缓存策略
    public class DiskCache implements BitmapCache {
        //缓存路径
        private String cacheDir = "Image";
        //MB
        private static final int MB = 1024 * 1024;
        private DiskLruCache diskLruCache;
        //单利
        private static DiskCache instance;
    
        private DiskCache(Context context) {
            initDiskCache(context);
        }
    
        private void initDiskCache(Context context) {
            //缓存目录
            File dir = getDiskCache(cacheDir, context);
            if (!dir.exists()) {
                dir.mkdir();
            }
            try {
                //设置缓存容量
                diskLruCache = DiskLruCache.open(dir, 1, 1, 50 * MB);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private File getDiskCache(String cacheDir, Context context) {
            //默认存储路径
            return new File(Environment.getExternalStorageDirectory(), cacheDir);
        }
    
        public static DiskCache getInstance(Context context) {
            if (instance == null) {
                synchronized (DiskCache.class) {
                    if (instance == null) {
                        instance = new DiskCache(context);
                    }
                }
            }
            return instance;
        }
    
        @Override
        public void put(BitmapRequest request, Bitmap bitmap) {
            DiskLruCache.Editor editor = null;
            OutputStream outputStream = null;
            try {
                editor = diskLruCache.edit(request.getImageUrlMD5());
                //一个key对应一个文件
                outputStream = editor.newOutputStream(0);
                if (persistBitmap2Disk(bitmap, outputStream)) {
                    editor.commit();
                } else {
                    editor.abort();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private boolean persistBitmap2Disk(Bitmap bitmap, OutputStream outputStream) {
            BufferedOutputStream bos = new BufferedOutputStream(outputStream);
            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) {
            try {
                DiskLruCache.Snapshot snapshot = diskLruCache.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) {
            try {
                diskLruCache.remove(request.getImageUrlMD5());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    //内存缓存策略
    public class MemoryCache implements BitmapCache {
    
        private LruCache<String, Bitmap> lruCache;
    
        public MemoryCache() {
            //设置最大缓存值
            int maxSize = (int) (Runtime.getRuntime().freeMemory() / 1024 / 8);
            lruCache = new LruCache<String, Bitmap>(maxSize) {
                //告诉如何计算
                @Override
                protected int sizeOf(String key, Bitmap value) {
                    return value.getRowBytes() * value.getHeight();
                }
            };
        }
    
        @Override
        public void put(BitmapRequest request, Bitmap bitmap) {
            lruCache.put(request.getImageUrlMD5(), bitmap);
        }
    
        @Override
        public Bitmap get(BitmapRequest request) {
            return lruCache.get(request.getImageUrlMD5());
        }
    
        @Override
        public void remove(BitmapRequest request) {
            lruCache.remove(request.getImageUrlMD5());
        }
    }
    
    public class DoubleCache implements BitmapCache{
    
        //内存缓存
        private MemoryCache memoryCache = new MemoryCache();
        //硬盘缓存
        private DiskCache diskCache;
    
        public DoubleCache(Context context){
            diskCache = DiskCache.getInstance(context);
        }
    
        @Override
        public void put(BitmapRequest request, Bitmap bitmap) {
            memoryCache.put(request,bitmap);
            diskCache.put(request,bitmap);
        }
    
        @Override
        public Bitmap get(BitmapRequest request) {
            Bitmap bitmap = memoryCache.get(request);
            if(bitmap == null){
                bitmap = diskCache.get(request);
                if(bitmap != null){
                    //放在内存,方便读取
                    memoryCache.put(request,bitmap);
                }
            }
            return bitmap;
        }
    
        @Override
        public void remove(BitmapRequest request) {
            memoryCache.remove(request);
            diskCache.remove(request);
        }
    }
    

    硬盘缓存调用了一个开源库DiskLruCache,用到了其中的DiskLruCache.java,IOUtils.javaStrictLineReader.java
    接下来时加载策略,包括加载策略接口LoadPolicy,ReversePolicy和SerialPolicy

    //加载策略接口
    public interface LoadPolicy {
        //优先级比较
        int compareto(BitmapRequest request1,BitmapRequest request2);
    }
    
    //逆序加载策略
    public class ReversePolicy implements LoadPolicy{
        @Override
        public int compareto(BitmapRequest request1, BitmapRequest request2) {
            return request2.getSerialNo() - request1.getSerialNo();
        }
    }
    
    //顺序加载策略
    public class SerialPolicy implements LoadPolicy{
        @Override
        public int compareto(BitmapRequest request1, BitmapRequest request2) {
            return request1.getSerialNo() - request2.getSerialNo();
        }
    }
    

    然后是工具类,包括图片解码类BitmapDecoder,图片宽高计算类ImageViewHelper和MD5工具类MD5Utils

    //解码图片
    public abstract class BitmapDecoder {
        public Bitmap decodeBitmap(int reqWidth, int reqHeight) {
            //初始化Options
            BitmapFactory.Options options = new BitmapFactory.Options();
            //读取部分信息,获得图片宽高
            options.inJustDecodeBounds = true;
            //根据bitmap加载图片
            decodeBitmapWithOption(options);
            //计算图片缩放比例
            caculateSizeWithOption(options, reqWidth, reqHeight);
            //返回缩放后的Bitmap
            return decodeBitmapWithOption(options);
        }
    
        private void caculateSizeWithOption(BitmapFactory.Options options, int reqWidth, int reqHeight) {
            int width = options.outWidth;
            int height = options.outHeight;
            int inSampleSize = 1;
            if (width > reqWidth || height > reqHeight) {
                int widthRatio = Math.round((float) width / (float) reqWidth);
                int heightRatio = Math.round((float) height / (float) reqHeight);
                inSampleSize = Math.max(widthRatio, heightRatio);
            }
            options.inSampleSize = inSampleSize;
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            options.inJustDecodeBounds = false;
            //内存不足时回收Bitmap
            options.inPurgeable = true;
            options.inInputShareable = true;
        }
    
        public abstract Bitmap decodeBitmapWithOption(BitmapFactory.Options options);
    }
    
    public class ImageViewHelper {
    
    	//默认的图片宽高
    	private static int DEFAULT_WIDTH = 200;
    	private static int DEFAULT_HEIGHT = 200;
    	
    	//获取ImageView控件的宽度
    	public static int getImageViewWidth(ImageView imageView){
    		if(imageView != null){
    			LayoutParams params = imageView.getLayoutParams();
    			int width = 0;
    			if(params != null && params.width != LayoutParams.WRAP_CONTENT){
    				width = imageView.getWidth();
    			}
    			if(width <= 0 && params != null){
    				width = params.width;
    			}
    			if(width <= 0){
    				width = getImageViewFieldValue(imageView,"mMaxWidth");
    			}
    			return width;
    		}
    		return DEFAULT_WIDTH;
    	}
    	
    	//获取图片的高度
    	public static int getImageViewHeight(ImageView imageView){
    		if(imageView != null){
    			LayoutParams params = imageView.getLayoutParams();
    			int height = 0;
    			if(params != null && params.height != LayoutParams.WRAP_CONTENT){
    				height = imageView.getWidth();
    			}
    			if(height <= 0 && params != null){
    				height = params.height;
    			}
    			if(height <= 0){
    				height = getImageViewFieldValue(imageView,"mMaxHeight");
    			}
    			return height;
    		}
    		return DEFAULT_HEIGHT;
    	}
    	
    	private static int getImageViewFieldValue(ImageView imageView,String fieldName) {
    		try {
    			Field field = ImageView.class.getDeclaredField(fieldName);
    			field.setAccessible(true);
    			int fieldValue = (Integer)field.get(imageView);
    			if(fieldValue > 0 && fieldValue < Integer.MAX_VALUE){
    				return fieldValue;
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return 0;
    	}
    }
    
    public class MD5Utils {
        private static final String TAG = "MD5Utils";
        private static MessageDigest digest;
    
        static {
            try {
                digest = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
                Log.d(TAG, "MD5算法不支持");
            }
        }
    
        //MD5加密
        public static String toMD5(String key) {
            if (digest == null) {
                return String.valueOf(key.hashCode());
            }
            //更新字节
            digest.update(key.getBytes());
            //获取最终的摘要
            return convert2HexString(digest.digest());
        }
    
        //转为16进制字符串
        private static String convert2HexString(byte[] bytes) {
            StringBuffer sb = new StringBuffer();
            for (byte b : bytes) {
                String hex = Integer.toHexString(0xFF & b);
                if (hex.length() == 1) {
                    sb.append('0');
                }
                sb.append(hex);
            }
            return sb.toString();
        }
    }
    

    最后是加载类,包括加载接口Loader,加载抽象类AbstractLoader,硬盘加载器LocalLoader,网络加载器UrlLoader,空加载器NullLoader,图片加载器SimpeImageLoader,加载器管理LoaderManager

    //加载器接口
    public interface Loader {
        //加载图片
        void loadImage(BitmapRequest request);
    }
    
    //抽象加载器
    public abstract class AbstractLoader implements Loader {
        //持有缓存策略,得到自定义缓存策略
        private BitmapCache bitmapCache = SimpeImageLoader.getInstance().getConfig().getBitmapCache();
        //拿到显示配置
        private DisplayConfig displayConfig = SimpeImageLoader.getInstance().getConfig().getDisplayConfig();
    
        @Override
        public void loadImage(BitmapRequest request) {
            //从缓存中读取bitmap
            Bitmap bitmap = bitmapCache.get(request);
            if (bitmap == null) {
                //显示默认加载图片
                showLoadingImage(request);
                //加载图片
                bitmap = onLoad(request);
                //缓存图片
                cacheBitmap(request, bitmap);
            }
            deliveryToUIThread(request, bitmap);
        }
    
        //交给主线程显示
        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);
                    }
                });
            }
        }
    
        //更新ImageView
        private void updateImageView(BitmapRequest request, Bitmap bitmap) {
            ImageView imageView = request.getImageView();
            //加载正常
            if (bitmap != null && imageView.getTag().equals(request.getImageUrl())) {
                imageView.setImageBitmap(bitmap);
            }
            //加载失败
            if (bitmap == null && request.getDisplayConfig() != null &&
                    request.getDisplayConfig().failedImage != -1) {
                imageView.setImageResource(displayConfig.failedImage);
            }
            //监听 回调
            if (request.imageListener != null) {
                request.imageListener.onComplete(imageView, bitmap, request.getImageUrl());
            }
        }
    
        //缓存图片
        private void cacheBitmap(BitmapRequest request, Bitmap bitmap) {
            if (request != null && bitmap != null) {
                synchronized (AbstractLoader.class) {
                    bitmapCache.put(request, bitmap);
                }
            }
        }
    
        //抽象的加载方法,由子类去实现
        protected abstract Bitmap onLoad(BitmapRequest request);
    
        //加载前显示的图片
        protected void showLoadingImage(BitmapRequest request) {
            if (hasLoadingPlaceHolder()) {
                final ImageView imageView = request.getImageView();
                if (imageView != null) {
                    imageView.post(new Runnable() {
                        @Override
                        public void run() {
                            imageView.setImageResource(displayConfig.loadingImage);
                        }
                    });
                }
            }
        }
    
        protected boolean hasLoadingPlaceHolder() {
            return (displayConfig != null && displayConfig.loadingImage > 0);
        }
    
        protected boolean hasFailedPlaceHolder() {
            return (displayConfig != null && displayConfig.failedImage > 0);
        }
    }
    
    //硬盘加载器
    public class LocalLoader extends AbstractLoader{
        @Override
        protected Bitmap onLoad(BitmapRequest request) {
            //得到本地图片路径
            final String path = Uri.parse(request.getImageUrl()).getPath();
            File file = new File(path);
            if(!file.exists()){
                return null;
            }
            BitmapDecoder decoder = new BitmapDecoder() {
                @Override
                public Bitmap decodeBitmapWithOption(BitmapFactory.Options options) {
                    return BitmapFactory.decodeFile(path,options);
                }
            };
            return decoder.decodeBitmap(ImageViewHelper.getImageViewWidth(request.getImageView()),
                    ImageViewHelper.getImageViewHeight(request.getImageView()));
        }
    }
    
    //网络加载器
    public class UrlLoader extends AbstractLoader {
        @Override
        protected Bitmap onLoad(final BitmapRequest request) {
            //下载之后读取
            downloadImgByUrl(request.getImageUrl(), getCache(request.getImageUrlMD5()));
            BitmapDecoder decoder = new BitmapDecoder() {
                @Override
                public Bitmap decodeBitmapWithOption(BitmapFactory.Options options) {
                    return BitmapFactory.decodeFile(getCache(request.getImageUrlMD5()).getAbsolutePath(), options);
                }
            };
            return decoder.decodeBitmap(ImageViewHelper.getImageViewWidth(request.getImageView())
                    , ImageViewHelper.getImageViewHeight(request.getImageView()));
        }
    
        public static boolean downloadImgByUrl(String urlStr, File file) {
            FileOutputStream fos = null;
            InputStream is = null;
            try {
                URL url = new URL(urlStr);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                is = conn.getInputStream();
                fos = new FileOutputStream(file);
                byte[] buf = new byte[512];
                int len = 0;
                while ((len = is.read(buf)) != -1) {
                    fos.write(buf, 0, len);
                }
                fos.flush();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (is != null)
                        is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (fos != null)
                        fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return false;
        }
    
        private File getCache(String unipue) {
            File file = new File(Environment.getExternalStorageDirectory(), "ImageLoader");
            if (!file.exists()) {
                file.mkdir();
            }
            return new File(file, unipue);
        }
    }
    
    public class NullLoader extends AbstractLoader {
        @Override
        protected Bitmap onLoad(BitmapRequest request) {
            return null;
        }
    }
    
    //图片加载器,单利对象
    public class SimpeImageLoader {
        //配置文件
        private ImageLoaderConfig config;
        //请求队列
        private RequestQueue queue;
        //单利
        private static volatile SimpeImageLoader instance;
    
        private SimpeImageLoader() {
        }
    
        private SimpeImageLoader(ImageLoaderConfig config) {
            this.config = config;
            queue = new RequestQueue(config.getThreadCount());
            //开启请求队列
            queue.start();
        }
    
        public static SimpeImageLoader getInstance(ImageLoaderConfig config) {
            if (instance == null) {
                synchronized (SimpeImageLoader.class) {
                    if (instance == null) {
                        instance = new SimpeImageLoader(config);
                    }
                }
            }
            return instance;
        }
    
        //第二次获取单利
        public static SimpeImageLoader getInstance() {
            if (instance == null) {
                throw new UnsupportedOperationException("未初始化参数");
            }
            return instance;
        }
    
        //获取全局配置
        public ImageLoaderConfig getConfig(){
            return config;
        }
    
        //获取图片
        public void displayImage(ImageView imageView, String url) {
            displayImage(imageView, url, null, null);
        }
    
        //扩展,重载
        public void displayImage(ImageView imageView, String url,
                                 DisplayConfig displayConfig, ImageListener imageListener) {
            //实例化请求
            BitmapRequest bitmapRequest = new BitmapRequest(imageView,url,displayConfig,imageListener);
            //添加请求到队列
            queue.addRequest(bitmapRequest);
    
        }
    
        //扩展接口
        public interface ImageListener {
            void onComplete(ImageView imageView, Bitmap bitmap, String url);
        }
    }
    
    //加载器管理
    public class LoaderManager {
        //缓存支持的Loader类型
        private Map<String, Loader> loaderMap = new HashMap<>();
        //单例模式
        private static LoaderManager instance = new LoaderManager();
    
        private LoaderManager() {
            register("http", new UrlLoader());
            register("https", new UrlLoader());
            register("file", new LocalLoader());
        }
    
        public static LoaderManager getInstance() {
            return instance;
        }
    
        private void register(String schema, Loader Loader) {
            loaderMap.put(schema, Loader);
        }
    
        public Loader getLoader(String schema){
            if(loaderMap.containsKey(schema)){
                return loaderMap.get(schema);
            }
            return new NullLoader();
        }
    }
    

    最后是测试类,这里我是用tomcat搭建服务器,使用975张图片做测试

    public class MainActivity extends AppCompatActivity {
    
        private SimpeImageLoader imageLoader;
        private static final int COUNT = 975;
        private static final String path = "http://192.168.1.2:8080/test/";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.list);
            GridView listview = (GridView) findViewById(R.id.listview);
            listview.setAdapter(new MyAdapter(this));
    
            //配置
            ImageLoaderConfig.Builder build = new ImageLoaderConfig.Builder();
            build.setThreadCount(3) //线程数量
                    .setLoadPolicy(new ReversePolicy()) //加载策略
                    .setCachePolicy(new DoubleCache(this)) //缓存策略
                    .setLoadingImage(R.drawable.loading)
                    .setFaildImage(R.drawable.not_found);
    
            ImageLoaderConfig config = build.build();
            //初始化
            imageLoader = SimpeImageLoader.getInstance(config);
        }
    
        class MyAdapter extends BaseAdapter {
    
            private LayoutInflater inflater;
    
            public MyAdapter(Context context) {
                inflater = LayoutInflater.from(context);
            }
    
            @Override
            public int getCount() {
                return COUNT;
            }
    
            @Override
            public Object getItem(int position) {
                return getUrl(position);
            }
    
            @Override
            public long getItemId(int position) {
                return position;
            }
    
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                View item = inflater.inflate(R.layout.item, null);
                ImageView imageView = (ImageView) item.findViewById(R.id.iv);
                //请求图片
                imageLoader.displayImage(imageView, getUrl(position));
                return item;
            }
        }
    
        public String getUrl(int position) {
            if (position < 10)
                return path + "00" + position + ".jpg";
            else if (position < 100)
                return path + "0" + position + ".jpg";
            else if (position < COUNT)
                return path + position + ".jpg";
            else
                return null;
        }
    }
    

    其实,这就是缩水版的Glide

  • 相关阅读:
    physicsbased animation阅读计划
    读代码的一点感想
    Paired Joint Coordinates
    坐标变换
    ADO.NET用法示例
    希腊字母读法
    数据库系统概论(第三版)学习笔记
    在网页里让文本框只能输入数字的一种方法。外加回车换Tab (javascript key键的使用)+禁止切换输入法转
    常用的一些javascript小技巧
    在.NET2.0中上传文件操作(解决了上传文件大小和多文件限制)转
  • 原文地址:https://www.cnblogs.com/cj5785/p/10664610.html
Copyright © 2020-2023  润新知