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

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

思路

在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

原文地址:https://www.cnblogs.com/cj5785/p/10664610.html