多应用+插件架构,代码干净,支持一键云编译,码云点赞13K star,4.8-4.12 预售价格198元 广告
[TOC] # Glide使用 简单使用: ```java @Override public void onCreate(Bundle savedInstanceState) { //... ImageView imageView = (ImageView) findViewById(R.id.my_image_view); Glide.with(this) .load("http://goo.gl/gEgYUd") .asBitmap() // 只允许加载静态图片,同理有asGif()方法 .placeholder(R.drawable.loading) // 占位图 .error(R.drawable.error) // 异常占位图 .override(100, 100) // 指定加载图片的尺寸 .into(imageView); } ``` 其中,load方法有以下几种重载: ```java // 从字节数组加载 public DrawableTypeRequest<byte[]> load(byte[] model) {} // 从File对象加载 public DrawableTypeRequest<File> load(File file) {} // 从资源文件加载 public DrawableTypeRequest<Integer> load(Integer resourceId) {} // 从File路径或UriLoader构造的url、uri来加载 public DrawableTypeRequest<String> load(String string) {} // 从ModelLoaderFactory生成的model来加载 public <T> DrawableTypeRequest<T> load(T model) {} // 从UriLoader生成的Uri来加载 public DrawableTypeRequest<Uri> load(Uri uri) {} ``` # Glide源码分析 ## with方法 with方法的主要作用有两个,一是获得一个RequestManager对象,二是确定Glide的生命周期,当传入全局Application Context时,Glide生命周期等同于应用生命周期;当传入Activity或Fragment时,Glide会向当前Activity中添加一个隐藏的Fragment,来确定自身的生命周期,比如加载图片过程中Activity被销毁了,隐藏的Fragment是可以感知的,这样Glide的加载就可以同步终止。 with方法有以下几个重载: ```java public static RequestManager with(Context context) {} public static RequestManager with(Activity activity) {} public static RequestManager with(FragmentActivity activity) {} public static RequestManager with(Fragment fragment) {} ``` 每一种方法都是根据入参获取RequestManager: ```java public static RequestManager with(Context context) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(context); } ``` 下面看看retriever的get方法: ```java public RequestManager get(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) { return get(((ContextWrapper) context).getBaseContext()); } } return getApplicationManager(context); } ``` 可以看到,当前为主线程且Context非Application时是一种处理方式,其他是一种处理方式。当context为Application时,直接创建RequestManager对象: ```java private RequestManager getApplicationManager(Context context) { if (applicationManager == null) { synchronized (this) { if (applicationManager == null) { applicationManager = new RequestManager(context.getApplicationContext(), new ApplicationLifecycle(), new EmptyRequestManagerTreeNode()); } } } return applicationManager; } ``` 而当Context为Activity、Fragment、FragmentActivity时,处理如下: ```java public RequestManager get(FragmentActivity activity) { if (Util.isOnBackgroundThread()) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); FragmentManager fm = activity.getSupportFragmentManager(); return supportFragmentGet(activity, fm); } } ``` 可以看到,如果是在子线程的话,就按Context为Application来进行处理。否则调用supportFragmentGet方法来创建RequestManager: ```java RequestManager supportFragmentGet(Context context, FragmentManager fm) { SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm); RequestManager requestManager = current.getRequestManager(); if (requestManager == null) { requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode()); current.setRequestManager(requestManager); } return requestManager; } ``` 上面代码中SupportRequestManagerFragment就是要添加的隐藏Fragment。 ## load方法 load方法是RequestManager中的方法,有多个重载,上文已经提到,下面我们看看其中的一个重载方法: ```java public DrawableTypeRequest<String> load(String string) { return (DrawableTypeRequest<String>) fromString().load(string); } ``` ```java public DrawableTypeRequest<String> fromString() { return loadGeneric(String.class); } ``` ```java private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) { ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context); ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader = Glide.buildFileDescriptorModelLoader(modelClass, context); if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) { throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for" + " which there is a registered ModelLoader, if you are using a custom model, you must first call" + " Glide#register with a ModelLoaderFactory for your custom model class"); } return optionsApplier.apply( new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context, glide, requestTracker, lifecycle, optionsApplier)); } ``` 在loadGeneric方法中,会生成ModelLoader对象,ModelLoader对象时用于加载图片的,根据load方法传入的参数不同,会得到不同的ModelLoader对象,刚刚传入的是String.class对象,因此得到的是StreamStringLoader对象,实现了ModelLoader接口。 loadGeneric方法最后创建了一个DrawableTypeRequest对象,DrawableTypeRequest类主要是提供了asBitmap和asGif两个方法,这两个方法会返回BitmapTypeRequest和GifTypeRequest,当然不调用asBitmap和asGif的情况下,默认返回DrawableTypeRequest对象。 接下来看看fromString后的load方法,由于在DrawableTypeRequest类中没有load方法,因此到其父类DrawableRequestBuilder中看看,在该类源码中,可以看到load、placeholder、error、centerCrop等等方法,基本就是我们常用的那些。 ## into方法 into方法同样是在DrawableRequestBuilder中定义的: ```java public Target<GlideDrawable> into(ImageView view) { return super.into(view); } ``` 调用了父类的方法: ```java public Target<TranscodeType> into(ImageView view) { //... return into(glide.buildImageViewTarget(view, transcodeClass)); } ``` 先是调用glide.buildImageViewTarget()方法构建出一个Target对象,Target对象是用来最终展示图片用的。来看看是怎么构建出的: ```java <R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) { return imageViewTargetFactory.buildTarget(imageView, transcodedClass); } ``` ```java public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) { if (GlideDrawable.class.isAssignableFrom(clazz)) { return (Target<Z>) new GlideDrawableImageViewTarget(view); } else if (Bitmap.class.equals(clazz)) { return (Target<Z>) new BitmapImageViewTarget(view); } else if (Drawable.class.isAssignableFrom(clazz)) { return (Target<Z>) new DrawableImageViewTarget(view); } else { throw new IllegalArgumentException("Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)"); } } ``` 会根据传入的class参数来构建不同的Target对象,我们在使用Glide加载图片的时候调用了asBitmap()方法,那么这里就会构建出BitmapImageViewTarget对象,否则的话构建的都是GlideDrawableImageViewTarget对象。其中的DrawableImageViewTarget对象通常都是用不到的。 接下来回来看看拿到Target对象后调用的into方法: ```java public <Y extends Target<TranscodeType>> Y into(Y target) { //... Request request = buildRequest(target); target.setRequest(request); lifecycle.addListener(target); requestTracker.runRequest(request); return target; } ``` 首先调用buildRequest方法出那个键一个Request对象,然后再执行这个Request。Request在Glide中是用来发出加载图片请求的,是一个非常关键的组件。先来看看Request是如何build出来的: ```java private Request buildRequest(Target<TranscodeType> target) { return buildRequestRecursive(target, null); } ``` ```java private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) { //前面省略处理缩略图相关代码 return obtainRequest(target, sizeMultiplier, priority, parentCoordinator); } ``` ```java private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority, RequestCoordinator requestCoordinator) { return GenericRequest.obtain( loadProvider, model, signature, context, priority, target, sizeMultiplier, placeholderDrawable, placeholderId, errorPlaceholder, errorId, fallbackDrawable, fallbackResource, requestListener, requestCoordinator, glide.getEngine(), transformation, transcodeClass, isCacheable, animationFactory, overrideWidth, overrideHeight, diskCacheStrategy); } ``` 这里调用了GenericRequest的obtain方法: ```java public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain( LoadProvider<A, T, Z, R> loadProvider, A model, Key signature, Context context, Priority priority, Target<R> target, float sizeMultiplier, Drawable placeholderDrawable, int placeholderResourceId, Drawable errorDrawable, int errorResourceId, Drawable fallbackDrawable, int fallbackResourceId, RequestListener<? super A, R> requestListener, RequestCoordinator requestCoordinator, Engine engine, Transformation<Z> transformation, Class<R> transcodeClass, boolean isMemoryCacheable, GlideAnimationFactory<R> animationFactory, int overrideWidth, int overrideHeight, DiskCacheStrategy diskCacheStrategy) { @SuppressWarnings("unchecked") GenericRequest<A, T, Z, R> request = (GenericRequest<A, T, Z, R>) REQUEST_POOL.poll(); if (request == null) { request = new GenericRequest<A, T, Z, R>(); } request.init(loadProvider, model, signature, context, priority, target, sizeMultiplier, placeholderDrawable, placeholderResourceId, errorDrawable, errorResourceId, fallbackDrawable, fallbackResourceId, requestListener, requestCoordinator, engine, transformation, transcodeClass, isMemoryCacheable, animationFactory, overrideWidth, overrideHeight, diskCacheStrategy); return request; } ``` 可以看到obtain方法实际就是获得一个GenericRequest对象,同时对成员变量进行初始化。 拿到Request后,我们看看requestTracker.runRequest()方法中Request是怎么执行的: ```java public void runRequest(Request request) { requests.add(request); //... request.begin(); } ``` 下面看看GenericRequest的begin方法: ```java public void begin() { startTime = LogTime.getLogTime(); if (model == null) { onException(null); return; } status = Status.WAITING_FOR_SIZE; if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { target.getSize(this); } if (!isComplete() && !isFailed() && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); } if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } } ``` 在begin方法中,我们主要关注以下几行代码: * onException(null); * onSizeReady(overrideWidth, overrideHeight); * target.getSize(this); * target.onLoadStarted(getPlaceholderDrawable()); 下面对这四个方法我们一个一个分析 1、onException(null); 可以看到,如果model等于null,也就是在load方法中传入的图片URL地址为空,会调用onException方法,onException方法中会调用setErrorPlaceholder方法: ```java public void onException(Exception e) { //... setErrorPlaceholder(e); } ``` ```java private void setErrorPlaceholder(Exception e) { Drawable error = model == null ? getFallbackDrawable() : null; if (error == null) { error = getErrorDrawable(); } if (error == null) { error = getPlaceholderDrawable(); } target.onLoadFailed(e, error); } ``` 会先去获取一个error的占位图,获取不到的话会去获取一个loading的占位图,然后调用target.onLoadFailed方法并传入占位图: ```java public void onLoadFailed(Exception e, Drawable errorDrawable) { view.setImageDrawable(errorDrawable); } ``` 将占位图设置到ImageView即可。 2、target.onLoadStarted(getPlaceholderDrawable()); ```java public void onLoadStarted(Drawable placeholder) { view.setImageDrawable(placeholder); } ``` 设置占位图的代码也很简单 3、onSizeReady()和target.getSize() 当使用了override方法给图片指定了固定宽高时,调用onSizeReady()方法;没指定时,调用target.getSize()方法,target.getSize()方法的内部会根据ImageView的layout\_width和layout\_height值做一系列的计算,来算出图片应该的宽高,然后再根据宽高调用onSizeReady()方法。那么来看看onSizeReady()方法: ```java ``` # Glide缓存原理 ## 缓存机制 Glide的缓存机制,主要分为2种缓存,一种是内存缓存,一种是磁盘缓存。 使用内存缓存的原因是:防止应用重复将图片读入到内存,造成内存资源浪费;使用磁盘缓存的原因是:防止应用重复的从网络或者其他地方下载和读取数据。正是因为有着这两种缓存的结合,才构成了Glide极佳的缓存效果。 ## 缓存算法 1、LruCache最近最少使用算法,设定一个缓存大小,当缓存达到这个大小之后,会将最老的数据移除,避免图片占用内存过大导致OOM。LruCache 内部用LinkHashMap存取数据,在双向链表保证数据新旧顺序的前提下,设置一个最大内存,往里面put数据的时候,当数据达到最大内存的时候,将最老的数据移除掉,保证内存不超过设定的最大值。 2、LinkHashMap继承HashMap,在 HashMap的基础上,新增了双向链表结构,每次访问数据的时候,会更新被访问的数据的链表指针,具体就是先在链表中删除该节点,然后添加到链表头header之前,这样就保证了链表头header节点之前的数据都是最近访问的(从链表中删除并不是真的删除数据,只是移动链表指针,数据本身在map中的位置是不变的)。 ### 三级缓存原理 1、读取一张图片的时候,获取顺序:Lru算法缓存-》弱引用缓存-》磁盘缓存(如果设置了的话)。 当我们的APP中想要加载某张图片时: * 先去LruCache中寻找图片,如果LruCache中有,则直接取出来使用,并将该图片放入WeakReference中 * 如果LruCache中没有,则去WeakReference中寻找,如果WeakReference中有,则从WeakReference中取出图片使用 * 如果WeakReference中也没有图片,则从磁盘缓存/网络中加载图片 2、将图片缓存的时候,写入顺序:弱引用缓存-》Lru算法缓存-》磁盘缓存中。 * 当图片不存在的时候,先从网络下载图片,然后将图片存入弱引用中,glide会采用一个acquired(int)变量用来记录图片被引用的次数, 当acquired变量大于0的时候,说明图片正在使用中,也就是将图片放到弱引用缓存当中; * 如果acquired变量等于0了,说明图片已经不再被使用了,那么此时会调用方法来释放资源,首先会将缓存图片从弱引用中移除,然后再将它put到LruResourceCache当中 * 这样也就实现了正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存的功能。 # 总结 1、Glide的with方法两个作用,一是获得RequestManager对象,二是确定Glide的生命周期。 # 其他 ## Bitmap的高效加载 1、图片加载: Bitmap在Android中指的是一张图片,BitmapFactory提供了四类方法:decodeFile、decodeResource、decodeStream和decodeByteArray,分别用于从文件系统、资源、输入流以及字节数组中加载出一个Bitmap对象。其中decodeFile和decodeResource方法又简介调用了decodeStream方法,最终调用native方法实现加载。 2、按需加载 高效加载Bitmap的核心思想是采用BitmapFactory.Options来按需加载相应尺寸的图片。当原图很大,需要展示图片的ImageView很小时,通过BitmapFactory.Options按一定采样率来加载缩小后的图片到内存,再将其设置给ImageView,这样可以降低内存占用,避免OOM,提高Bitmap的加载性能。 3、采样率 采样率inSampleSize的取值应该总是为2的指数,如不是系统会自动向下取整。 a、将BitmapFactory.Options的inJustDecodeBounds参数设置为true并加载图片,此时只会解析图片宽高,不会真正加载到内存 b、取出宽高信息,根据采样率规则结合目标View的大小计算出采样率inSampleSize c、将BitmapFactory.Options的inJustDecodeBounds参数设置为true,重新加载图片 ## 内存泄漏问题 ## 大图加载框架 # 参考文档 [https://blog.csdn.net/guolin\_blog/article/details/53939176#commentBox](https://blog.csdn.net/guolin_blog/article/details/53939176#commentBox) [https://juejin.im/post/5dd766e1e51d45233c7e857f](https://juejin.im/post/5dd766e1e51d45233c7e857f) [https://juejin.im/post/5dbeda27e51d452a161e00c8](https://juejin.im/post/5dbeda27e51d452a161e00c8)