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

    xiaoxiao2024-04-05  128

    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

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

     

    最新回复(0)