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
有什么问题可以加我的微信私聊