Glide是Android端开源图片加载库,能够帮助我们下载、缓存、展示多种格式图片。也是现在主流图片加载框架之一。源码内部究竟是如何实现的呢?讲解主流程,简略分析。
用法如下:
Glide.with(context).load(url).into(imageView);
我这里拆分为三步分析:
一、with(context)点击源码查看到是多个重载方法activity、fragment、view等等,下面用其中一个方法来展示
@NonNull public static RequestManager with(@NonNull Activity activity) { return getRetriever(activity).get(activity); }
@NonNull private static RequestManagerRetriever getRetriever(@Nullable Context context) { // Context could be null for other reasons (ie the user passes in null), but in practice it will // only occur due to errors with the Fragment lifecycle. Preconditions.checkNotNull( context, "You cannot start a load on a not yet attached View or a Fragment where getActivity() " + "returns null (which usually occurs when getActivity() is called before the Fragment " + "is attached or after the Fragment is destroyed)."); return Glide.get(context).getRequestManagerRetriever(); }
调用getRetriever方法获取RequestManagerRetriever对象。在创建该对象之前首先通过Glide.java中的get方法获得了Glide单例对象以及AppClideModule等配置。
@NonNull public static Glide get(@NonNull Context context) { if (glide == null) { GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules(context.getApplicationContext()); synchronized (Glide.class) { if (glide == null) { checkAndInitializeGlide(context, annotationGeneratedModule); } } } return glide; }
下面的get方法可知道,在子线程不会添加生命周期;主线程添加一个空白的fragment来处理生命周期。最后返回RequestManager对象
@NonNull public RequestManager get(@NonNull Context context) { if (context == null) { throw new IllegalArgumentException("You cannot start a load on a null Context"); } else if (Util.isonMainThread() && !(context instanceof Application)) { if (context instanceof FragmentActivity) { return get((FragmentActivity) context); } else if (context instanceof Activity) { return get((Activity) context); } else if (context instanceof ContextWrapper // only unwrap a ContextWrapper if the baseContext has a non-null application context. // Context#createPackageContext may return a Context without an Application instance, // in which case a ContextWrapper may be used to attach one. && ((ContextWrapper) context).getbaseContext().getApplicationContext() != null) { return get(((ContextWrapper) context).getbaseContext()); } } return getApplicationManager(context); }//调用get判断线程@NonNull public RequestManager get(@NonNull FragmentActivity activity) { if (Util.isonBackgroundThread()) { //子线程 return get(activity.getApplicationContext()); } else { //主线程添加生命周期 assertNotDestroyed(activity); FragmentManager fm = activity.getSupportFragmentManager(); return supportFragmentGet(activity, fm, null, isActivityVisible(activity)); } }
二、load(url)上面执行完成到这里已经拿到RequestManager对象,然后调用load(url)。看源码可知是多个重载方法,传不同类型的资源。最终拿到RequestBuilder对象
// RequestManager.java 的代码如下public RequestBuilder
上一步拿到了RequestBuilder对象,调用into可知有2个重载方法。into的参数就是最终显示的控件。
into方法内部代码分支很多,代码庞大,所以只需走主流程如何显示ImageView的实现即可。当into内部代码执行完成后回到 buildImageViewTarget方法,这个方法是显示使用的,通过Executors.mainThreadExecutor())来切主线程,最终显示控件。
return into( glideContext.buildImageViewTarget(view, transcodeClass), null, requestOptions, Executors.mainThreadExecutor());
点击到into内部源码如下:
private
这里处理请求
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
将请求对象装到集合中,并且有加锁处理,运用于多线程的并发请求。
url请求走如下:
网络请求完成callback.onDataReady(result),开始一步一步往回传数据。在这一系列过程中,进行了数据处理,比如:图片压缩等。 省略N步骤
// HttpUrlFetcher.java 代码如下 @Override public void loadData( @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { long startTime = LogTime.getLogTime(); try { InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders()); callback.onDataReady(result); } catch (IOException e) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Failed to load data for url", e); } callback.onLoadFailed(e); } finally { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)); } } }
最后回到了ImageViewTarget类,显示控件。这就是整体简略主流程。
@Override public void setDrawable(Drawable drawable) { view.setImageDrawable(drawable); }
四、缓存原理分析当加载图片会走2种方式:
1、是Http/IO ;
2、三级缓存策略
一级缓存:活动缓存 ,当前Activity退出缓存销毁。
二级缓存:LRU内存缓存 ,APP应用退出缓存销毁。
三级缓存:LRU磁盘缓存 ,一直存在。
一、缓存机制加载流程:获取顺序是,先从活动缓存取,如果没有就再去内存缓存取,如果还没是没有就再去磁盘缓存取,都没有就再去网络下载。
二、缓存介绍:(1) 活动缓存:Glide自己实现的一种缓存策略,将使用的对象存放在HashMap,里面使用的弱引用,不需要时立即移除及时释放资源。
(2)内存缓存:使用的LRU算法进行处理,核心是使用 linkedHashMap 实现,保存到内存中。
(3)磁盘缓存:使用的LRU算法进行处理,核心是使用 linkedHashMap 实现,保存到磁盘中。(Glide使用DiskLruCache实现,将图片进行的加密、压缩处理,所以文件读写比普通IO处理效率高)
LRU的原理:假设 maxSize =3,当第4个数据进入时,移除最先未使用的。画图理解一哈:
LruCache类实际上是对linkedHashMap进行的封装。上代码证明:
值得注意的是,第三个参数true代表访问排序
this.map = new linkedHashMap
示例场景:加入maxSize=3时,有新元素添加,此刻正回收1元素,刚好页面又使用1元素。这时候如果1元素被回收,就会找不到1元素从而崩溃。所以设计了活动缓存
增加的活动缓存区解决上面的问题,画图方便理解:
总结:1、当元素在使用时,将从内存缓存(二级缓存)移动到活动缓存(一级缓存);
2、当元素未使用时,将从活动缓存释放资源,然后把该元素从活动缓存移动到内存缓存;
三级缓存策略的使用总结:
1、优先从活动缓存读取
2、活动缓存没有,再内存缓存中读取
3、内存缓存没有,再去磁盘缓存读取
4、磁盘缓存没有,再去网络获取本地文件读取