带你深入理解Glide框架并手写Glide框架

Hi,大家好!我是冯朝野,老鹰课堂的创始人。后续我会将我的技术转化成课程传授给大家,现在还在努力策划中。希望到时候大家可以过来捧捧场。如果你看过我这篇文章过后有收获的,也可以成为我的粉丝,后续还有很多干货等着给大家呢?谢谢你们啦!那么接下来这一篇是针对你们面试中会遇到的问题!让你们完美对弈面试官所有有关图片加载的面试题,让你们玩转图片加载。

首先先来看看面试官如何提出问题的:

面试官:你们在项目中怎么加载图片的啊

我:我们的项目是用Glide、Picasso、ImageLoader做图片加载的

面试官:你知道他们之间的区别吗?你用的Glide有什么优势或者你为是什么要选用Glide

我:区别?哦哦--Glide比其他的图片加载框架更优秀

面试官:具体体现在哪里呢?或者内存管理上有什么区别

我:额额额--这个我不太清楚

面试官:那你看过Glide的源码吗?

我:没有

面对以上的面试题实际上是每个android工程师必面的面试题,因为图片加载是android开发中必须要处理的,无论大小,所以图片加载是一个非常重要的内容。那么你要去面试之前必须对Glide进行深入了解,理解透彻,让面试官感觉你是有料的,让他认为你是高级开发工程师。所以接下来我来带你们去深入理解Glide.

如果你读懂了Glide代码后,在读其他框架源码是非常有帮助的。因为他底层的思想是非常有用的。后续我继续讲解在其他框架中的使用让你们玩转所有框架,自己也能够写框架。好了,那就让我们进入重点把。

学习一门课程,一个技术,都必须遵循what->how->why,这三个步骤去学习,如果你根据我这三个步骤去理解,那么你的收获一定是很丰富的。

1.什么是Glide

Glide是一个快速高效的Android图片加载库,具备性能高,流式API的语法特点.作者bumptech

优势:

        1.可配置度高,自适应度高;

        2.支持多钟数据源,本地,网络,assets gif在Glide是支持的;

        3.高效缓存,支持memory和disk图片缓存,默认使用二级缓存

        4.高效处理Bitmap,使用Bitmap pool复用Bitmap

        5.图片加载过程可以监听

        6.生命周期集成至Glide

2.如何使用Glide

1).引入Glide到gradle中加载

implementation 'com.github.bumptech.glide:glide:3.7.0'

2).查阅Glide底层框架架构,了解其特性,特点等。

RequestOptions options = new RequestOptions();
options.centerCrop();
options.dontAnimate();
options.bitmapTransform(new RoundedCornersTransformation(imgView.getContext(),
                10, 0));
options.placeholder(placeholder);
options.diskCacheStrategy(DiskCacheStrategy.ALL);
options.error(error);
Glide.with(imgView.getContext()).load(url).apply(options).into(imgView);

Glide现在升级了很多内容,刚开始是架构师由请求、管理者、分发三个对象完成。现在使用的是Builder模式来构造对象,再由Node树形结构进行访问请求bitmap对象。

首先需要RequestOptions收集设置参数,然后通过apply设置到RequestBuilder对象中,从RequestBuilder来看他就是一个Builder对象,使用Builder模式构建参数数据。然后使用into将请求加入到队列中。当然,要了解这个模式之前,我们先了解Glide刚开始架构的模型是如何的,因为这种模式是通过第一种慢慢演变过来的。

给你们画一张图,让你们清晰明了解析Glide底层架构。

我们将Glide架构按照银行办理业务例子来进行解析,让你们更清晰明了,这里面有三个角色,业务人员、取号机、银行人员

取号机根据业务人员分配号码,银行人员处理业务人员的请求。根据手上是否有事呼叫业务人员。那么我们把业务人员称为BitmapRequest,取号机称为RequestManager,银行人员称为BitmapDispatcher。

3.自己搭建Glide框架读取网络图片

1).Glide对象通过with来构建BitmapRequest对象

public class Glide {

    // 创建请求
    public static BitmapRequest with(Context context) {
        return new BitmapRequest(context);
    }

}

2).BitmapRequest对象构建url、imageview、resId等,需要加的时候自己添加就行,根据项目架构

public class BitmapRequest {

    // 请求路径
    private String url;

    // 上下文
    private Context context;

    // 需要加载图片的控件
    private SoftReference<ImageView> imageView;

    // 占位图片
    private int resId;

    // 回调对象
    private RequestListener requestListener;

    // 图片的标识
    private String urlMd5;

    public BitmapRequest(Context context) {
        this.context = context;
    }

    // 链式调度
    // 加载url
    public BitmapRequest load(String url) {
        this.url = url;
        if (!TextUtils.isEmpty(url))
            this.urlMd5 = MD5.MD516(url);
        return this;
    }

    // 设置占位图片
    public BitmapRequest loadding(int resId) {
        this.resId = resId;
        return this;
    }

    // 设置监听器
    public BitmapRequest setListener(RequestListener requestListener) {
        this.requestListener = requestListener;
        return this;
    }

    // 显示图片的控件
    public void into(ImageView imageView) {
        imageView.setTag(urlMd5);
        this.imageView = new SoftReference<>(imageView);
        RequestManager.getInstance(context).addBitmapRequest(this);
    }

    public String getUrl() {
        return url;
    }

    public Context getContext() {
        return context;
    }

    public ImageView getImageView() {
        return imageView.get();
    }

    public int getResId() {
        return resId;
    }

    public RequestListener getRequestListener() {
        return requestListener;
    }

    public String getUrlMd5() {
        return urlMd5;
    }
}

在into这里比较复杂,我在这里说明一下,如果你回头看不懂可以到这里来看。

into这里是队列处理,RequestManager负责的是所有请求,将请求加入到队列中,在银行小姐姐(BitmapDispatcher)处理判断是否有业务人员,如果有就会去处理.

while (!isInterrupted()) {
   if (requestQueue == null) {
       continue;
   }
   try {
        BitmapRequest bitmapRequest = requestQueue.take();
        if (bitmapRequest == null) {
           continue;
        }
        // 设置占位图片
        showLoaddingImg(bitmapRequest);
        // 网络加载获取图片资源
        Bitmap bitmap = findBitmap(bitmapRequest);
        // 将图片显示到ImageView
        showImageView(bitmapRequest, bitmap);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

这个是在BitmapDispatcher中处理的,发现有请求就处理,在RequestManager会根据系统的线程数分配多个BitmapDispatcher来处理BitmapRequest,这样达到高效加载图片,Glide架构思想就在这里。具体看源码

3).RequestManager这个地方有点复杂,我会详细讲解。

public class RequestManager {

    private static RequestManager requestManager;
    private LinkedBlockingQueue<BitmapRequest> requestQueue = new LinkedBlockingQueue<>();
    private BitmapDispatcher[] bitmapDispatchers; // 管理者需要分配多少个处理器来处理请求,而处理器的多少是由手机的内存线程数量来分配的
    /**
     * 线程池管理线程
     */
    public ExecutorService executorService;

    // 上下文
    private Context context;

    public static RequestManager getInstance(Context context) {
        if (requestManager == null) {
            synchronized (DiskBitmapCache.class) {
                if (requestManager == null) {
                    requestManager = new RequestManager(context);
                }
            }
        }
        return requestManager;
    }

    private RequestManager(Context context) {
        this.context = context;
        // 初始化线程池
        initThreadExecutor();
        // 只有一个管理者,所有在这里启动最合适
        start();
    }

    public void initThreadExecutor() {
        int size = Runtime.getRuntime().availableProcessors();
        if (size <= 0) {
            size = 1;
        }
        size *= 2;
        executorService = Executors.newFixedThreadPool(size);
    }

    public void start() {
        stop();
        startAllDispatcher();
    }

    // 这里收集所有请求
    public void addBitmapRequest(BitmapRequest bitmapRequest) {
        if (bitmapRequest == null) {
            return;
        }
        if (!requestQueue.contains(bitmapRequest)) {
            requestQueue.add(bitmapRequest); // 将请求加入队列
        }
    }

    // 处理并开始所有的线程
    public void startAllDispatcher() {
        // 获取线程最大数量
        final int threadCount = Runtime.getRuntime().availableProcessors();
        bitmapDispatchers = new BitmapDispatcher[threadCount];
        if (bitmapDispatchers.length > 0) {
            for (int i = 0; i < threadCount; i++) {
                // 线程数量开辟的请求分发去抢请求资源对象,谁抢到了,就由谁去处理
                BitmapDispatcher bitmapDispatcher = new BitmapDispatcher(requestQueue, context);
                executorService.execute(bitmapDispatcher);
                // 将每个dispatcher放到数组中,方便统一处理
                bitmapDispatchers[i] = bitmapDispatcher;
            }
        }
    }

    // 停止所有的线程
    public void stop() {
        if (bitmapDispatchers != null && bitmapDispatchers.length > 0) {
            for (BitmapDispatcher bitmapDispatcher : bitmapDispatchers) {
                if (!bitmapDispatcher.isInterrupted()) {
                    bitmapDispatcher.interrupt(); // 中断
                }
            }
        }
    }

}

---1).LinkedBlockingQueue对象处理请求队列,在addBitmapRequest方法将BitmapRequest加入到队列中

---2).BitmapDispatcher这个是根据单个app最大的线程数创建BitmapDispatcher数组对象,最终达到高效率处理业务人员的请求,这样能够提高性能。

---3).ExecutorService通过线程池来高效处理BitmapDispatcher对象。

---4).由于RequestManager是单例,所以在使用了into加入队列后就需要将银行人员叫起来接客了。

4).BitmapDispatcher强队列中的请求然后去下载bitmap对象,然后设置ImageView对象。

public class BitmapDispatcher extends Thread {

    Handler handler = new Handler(Looper.getMainLooper());

    // 创建一个阻塞线程
    private LinkedBlockingQueue<BitmapRequest> requestQueue;

    // 获取三级缓存对象
    private DoubleLruCache doubleLruCache;

    public BitmapDispatcher(LinkedBlockingQueue<BitmapRequest> requestQueue, Context context) {
        this.requestQueue = requestQueue;
        doubleLruCache = new DoubleLruCache(context);
    }

    @Override
    public void run() {
        super.run();
        // 该线程没有被中断的时候
        while (!isInterrupted()) {
            if (requestQueue == null) {
                continue;
            }
            try {
                BitmapRequest bitmapRequest = requestQueue.take();
                if (bitmapRequest == null) {
                    continue;
                }
                // 设置占位图片
                showLoaddingImg(bitmapRequest);
                // 网络加载获取图片资源
                Bitmap bitmap = findBitmap(bitmapRequest);
                // 将图片显示到ImageView
                showImageView(bitmapRequest, bitmap);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void showImageView(final BitmapRequest bitmapRequest, final Bitmap bitmap) {
        final ImageView imageView = bitmapRequest.getImageView();
        if (bitmap != null && imageView != null && bitmapRequest.getUrlMd5().equals(imageView.getTag())) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    imageView.setImageBitmap(bitmap);
                    RequestListener requestListener = bitmapRequest.getRequestListener();
                    if (requestListener != null) {
                        requestListener.onSuccess(bitmap);
                    }
                }
            });
        } else {
            RequestListener requestListener = bitmapRequest.getRequestListener();
            if (requestListener != null) {
                requestListener.onFaile();
            }
        }
    }

    private Bitmap findBitmap(BitmapRequest bitmapRequest) {
        // 这里需要通过三级缓存缓存图片
        Bitmap bitmap = null;
        bitmap = doubleLruCache.get(bitmapRequest);
        // 三级缓存中都没有图片的时候去下载
        if (bitmap == null) {
            bitmap = downloadBitmao(bitmapRequest.getUrl());
            // 下载完成后放入三级缓存中
            if (bitmap != null) {
                doubleLruCache.put(bitmapRequest, bitmap);
            }
        }
        return bitmap;
    }

    private void showLoaddingImg(BitmapRequest bitmapRequest) {
        final ImageView imageView = bitmapRequest.getImageView();
        if (bitmapRequest.getResId() > 0 && imageView != null) {
            final int resId = bitmapRequest.getResId();
            handler.post(new Runnable() {
                @Override
                public void run() {
                    imageView.setImageResource(resId);
                }
            });
        }
    }

    private Bitmap downloadBitmao(String uri) {
        InputStream is = null;
        Bitmap bitmap = null;
        try {
            URL url = new URL(uri);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            is = urlConnection.getInputStream();
            bitmap = BitmapFactory.decodeStream(is);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return bitmap;
    }

}

这里是最终处理下载请求并设置ImageView对象的业务。这里采取了DoubleImageCache进行三级缓存,本地、缓存、网络三方即发处理业务。

    private Bitmap findBitmap(BitmapRequest bitmapRequest) {
        // 这里需要通过三级缓存缓存图片
        Bitmap bitmap = null;
        bitmap = doubleLruCache.get(bitmapRequest);
        // 三级缓存中都没有图片的时候去下载
        if (bitmap == null) {
            bitmap = downloadBitmao(bitmapRequest.getUrl());
            // 下载完成后放入三级缓存中
            if (bitmap != null) {
                doubleLruCache.put(bitmapRequest, bitmap);
            }
        }
        return bitmap;
    }

好了,到这里就结束了,源码我放到这里。

https://github.com/chenhongxin/Glide

有什么问题可以加我的微信私聊

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值