简单图片加载框架的打造(一)-框架设计

Posted by 夏敏的博客 on July 17, 2017

目前市场上有很多第三方图片加载框架, 当然,以UniversalImageLoader,Picasso,Glide为代表, 这些图片加载库大大方便了我们平时使用时需要图片加载地方的代码编写,且其性能还高.

之前学习了Volley的源码,生产者消费者模式的代码也看了很多,就想着试着自己打造一款图片加载框架,当然,功能与上面的比起来肯定有很多不足,不过锻炼一下框架思维也是不错的.

先贴工程路径,里面还有我准备写的数据库lib,还有httplib,只看ImageLoaderlib即可. https://github.com/Jerey-Jobs/Sherlock

简单图片加载框架需求

  1. 图片请求无需用户管理,分发器主动下载并设置图片
  2. 图片自适应ImageView大小,不能全部加载到内存
  3. TAG匹配,防止错位
  4. 灵活配置,加载时显示默认图片
  5. 自定义回调Callback,可让用户自己处理图片
  6. 图片加载策略选择,是后进优先加载还是先进优先加载
  7. 图片缓存策略配置,是否Disk缓存
  8. 能够主动取消请求.

暂时先定这么多需求,至于像Glide那样绑定住Activity或者Fragment的生命周期的功能,我们知道它是通过注册一个空的Fragment来干的,但是先不做了.上面需求已经够我们做的了.

我们可以想看一下我们设计的最终结果,其实还蛮让人期待的.

SherlockImageLoader.with(this)
        .setUrl("http://img.my.csdn.net/uploads/201407/26/1406382765_7341.jpg")
        .loadingImage(R.mipmap.ic_launcher_round)
        .errorImage(R.drawable.blog)
        .into(mImageView);

SherlockImageLoader.with(this)
        .setUrl("http://img.my.csdn.net/uploads/201407/26/1406382765_7341.jpg")
        .loadingImage(R.mipmap.ic_launcher_round)
        .errorImage(R.drawable.blog)
        .into(new SherlockImageLoader.Callback() {
            @Override
            public void onSuccess(Bitmap bitmap, String url) {
                Log.i(TAG, "onSuccess: " + bitmap + " url" + url);
            }
        });

框架设计

我将该图片框架命名为:SherlockImageLoader, 其架构很简单,一个入口,学习Volley的方式,那就是是分发器,加载器,缓存容器外加一个配置选项就行了.

框架如图:

sherlockimageloader

类的设计

根据上面的框架,我们需要新建SherlockImageLoader类作为主入口,提供displayImage()方法,然后呢,RequestQueue类提供添加请求的队列,RequestDispacher负责从队列取请求进行分发,分发即是调用Loader去加载请求, 加载请求是根据Config加载的. Loader同时还依赖缓存容器,因为每次去加载时,都需要看缓存是否已经有了.

跟着上面描述很简单.我们先开始写接口吧.

SherlockImageLoader我们先将其设置为单列,之后可以再改,入口的封装留到最后.

首先SherlockImageLoader需要持有一个队列和Config.还要提供显示方法给外部使用.代码如下.


public class SherlockImageLoader {

    private ImageLoaderConfig mImageLoaderConfig;
    private RequestQueue mRequestQueue;

    /**
     * 单列对象
     */
    private static volatile SherlockImageLoader instance;

    public SherlockImageLoader(Context context, ImageLoaderConfig imageLoaderConfig) {
        if (imageLoaderConfig != null) {
            mImageLoaderConfig = imageLoaderConfig;
        } else {
            mImageLoaderConfig = new ImageLoaderConfig.Builder()
                    .setBitmapCache(new DoubleCache(context))
                    .setLoadPolicy(new ReverseLoaderPolicy())
                    .build();
        }
        mRequestQueue = new RequestQueue(mImageLoaderConfig.getTHREAD_COUNT());
        mRequestQueue.start();
    }

    public ImageLoaderConfig getImageLoaderConfig() {
        return mImageLoaderConfig;
    }

    public RequestQueue getRequestQueue() {
        return mRequestQueue;
    }

    /**
     * 初始化
     * @param context
     */
    public static void init(Context context) {
        init(context, null);
    }

    public static void init(Context context, ImageLoaderConfig config) {
        if (instance == null) {
            instance = new SherlockImageLoader(context, config);
        }
    }

    public static SherlockImageLoader getInstance() {
        if (instance == null) {
            throw new RuntimeException("SherlockImageLoader must init");
        }

        return instance;
    }

    public void display(ImageView imageView, String url) {
        display(imageView, url, null, null);
    }


    public void display(String url, Callback callback) {
        display(null, url, callback, null);
    }

    /**
     * @param imageView
     * @param url
     * @param callback
     */
    public void display(ImageView imageView, String url, Callback callback, DisplayConfig
            displayConfig) {
        BitmapRequest bitmapRequest = new BitmapRequest(imageView, url, callback, displayConfig);
        mRequestQueue.addRequest(bitmapRequest);
    }


    /**
     * 供用户自定义使用
     */
    public static interface Callback {
        /**
         * @param bitmap
         * @param url
         */
        void onSuccess(Bitmap bitmap, String url);

    }
}

缓存器与分发器是具体业务类,下章节设计,先写一下Loader,Cache,Config的接口,

// Loader
public interface ILoader {
    void loadImage(BitmapRequest request);
}


/**
 * 加载策略
 * @author xiamin
 * @date 7/8/17.
 */
public interface ILoadPolicy {

    /**
     * 两个加载请求进行优先级比较
     * @param request1
     * @param request2
     * @return
     */
    int compareTo(BitmapRequest request1, BitmapRequest request2);
}

public interface BitmapCache {

    /**
     * 缓存bitmap
     * @param bitmapRequest
     * @param bitmap
     */
    void put(BitmapRequest bitmapRequest, Bitmap bitmap);

    /**
     * 通过请求取bitmap
     * @return
     */
    Bitmap get(BitmapRequest request);

    /**
     * 移除bitmap
     * @param request
     */
    void remove(BitmapRequest request);
}

思路很明朗到现在, 加载器就一个方法,加载请求, 策略呢也就是请求间的比较,谁大谁加载, 缓存呢就是存与取还有删除功能.

请求封装

我们的请求就是一个Bean类,里面的变量需要:

/** 图片软应用,内存不足情况下不加载 */
private SoftReference<ImageView> mImageViewSoftReference;
/** 图片路径 */
private String imageURL;
/** 图片的md5码,做缓存唯一标识,因为图片名可能是非法字符,合法化一下 */
private String imageUriMD5;
/** 编号,请求的唯一标识 */
private int serialNo;
/** 下载完监听 */
private SherlockImageLoader.Callback mCallback;
/** 显示设置,要是为空,则使用全局的 */
private DisplayConfig mDisplayConfig;
/** 请求tag,供取消请求时使用 */
private Object requestTag;
/** 是否被取消 */
private boolean isCancel = false;

并设置相应的get和set方法.

下一章

下一章将讲解分发器的设计:简单图片加载框架的打造(二)-分发器设计RequestQueue


本文作者:Anderson/Jerey_Jobs

博客地址 : http://jerey.cn/
简书地址 : Anderson大码渣
github地址 : https://github.com/Jerey-Jobs

作者:Anderson大码渣,欢迎关注我的简书: Anderson大码渣