[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)
- 导读
- Java知识
- Java基本程序设计结构
- 【基础知识】Java基础
- 【源码分析】Okio
- 【源码分析】深入理解i++和++i
- 【专题分析】JVM与GC
- 【面试清单】Java基本程序设计结构
- 对象与类
- 【基础知识】对象与类
- 【专题分析】Java类加载过程
- 【面试清单】对象与类
- 泛型
- 【基础知识】泛型
- 【面试清单】泛型
- 集合
- 【基础知识】集合
- 【源码分析】SparseArray
- 【面试清单】集合
- 多线程
- 【基础知识】多线程
- 【源码分析】ThreadPoolExecutor源码分析
- 【专题分析】volatile关键字
- 【面试清单】多线程
- Java新特性
- 【专题分析】Lambda表达式
- 【专题分析】注解
- 【面试清单】Java新特性
- Effective Java笔记
- Android知识
- Activity
- 【基础知识】Activity
- 【专题分析】运行时权限
- 【专题分析】使用Intent打开三方应用
- 【源码分析】Activity的工作过程
- 【面试清单】Activity
- 架构组件
- 【专题分析】MVC、MVP与MVVM
- 【专题分析】数据绑定
- 【面试清单】架构组件
- 界面
- 【专题分析】自定义View
- 【专题分析】ImageView的ScaleType属性
- 【专题分析】ConstraintLayout 使用
- 【专题分析】搞懂点九图
- 【专题分析】Adapter
- 【源码分析】LayoutInflater
- 【源码分析】ViewStub
- 【源码分析】View三大流程
- 【源码分析】触摸事件分发机制
- 【源码分析】按键事件分发机制
- 【源码分析】Android窗口机制
- 【面试清单】界面
- 动画和过渡
- 【基础知识】动画和过渡
- 【面试清单】动画和过渡
- 图片和图形
- 【专题分析】图片加载
- 【面试清单】图片和图形
- 后台任务
- 应用数据和文件
- 基于网络的内容
- 多线程与多进程
- 【基础知识】多线程与多进程
- 【源码分析】Handler
- 【源码分析】AsyncTask
- 【专题分析】Service
- 【源码分析】Parcelable
- 【专题分析】Binder
- 【源码分析】Messenger
- 【面试清单】多线程与多进程
- 应用优化
- 【专题分析】布局优化
- 【专题分析】绘制优化
- 【专题分析】内存优化
- 【专题分析】启动优化
- 【专题分析】电池优化
- 【专题分析】包大小优化
- 【面试清单】应用优化
- Android新特性
- 【专题分析】状态栏、ActionBar和导航栏
- 【专题分析】应用图标、通知栏适配
- 【专题分析】Android新版本重要变更
- 【专题分析】唯一标识符的最佳做法
- 开源库源码分析
- 【源码分析】BaseRecyclerViewAdapterHelper
- 【源码分析】ButterKnife
- 【源码分析】Dagger2
- 【源码分析】EventBus3(一)
- 【源码分析】EventBus3(二)
- 【源码分析】Glide
- 【源码分析】OkHttp
- 【源码分析】Retrofit
- 其他知识
- Flutter
- 原生开发与跨平台开发
- 整体归纳
- 状态及状态管理
- 零碎知识点
- 添加Flutter到现有应用
- Git知识
- Git命令
- .gitignore文件
- 设计模式
- 创建型模式
- 结构型模式
- 行为型模式
- RxJava
- 基础
- Linux知识
- 环境变量
- Linux命令
- ADB命令
- 算法
- 常见数据结构及实现
- 数组
- 排序算法
- 链表
- 二叉树
- 栈和队列
- 算法时间复杂度
- 常见算法思想
- 其他技术
- 正则表达式
- 编码格式
- HTTP与HTTPS
- 【面试清单】其他知识
- 开发归纳
- Android零碎问题
- 其他零碎问题
- 开发思路