Glide图片原理解析
1.Glide是什么?
Glide是Google在2014的IO大会发布一款图片处理框架,是目前android领域比较成熟的一款,也是Google官方推荐的图片处理框架,主要支持网络图片、二进制流、drawable资源、本地图片显示,还支持本地视频显示。
2.Glide基本功能使用
- 在app级别下面配置gradle
dependencies {
compile 'com.github.bumptech.glide:glide:3.7.0'
}
- 使用起来
通过Glide类进行一个链式调用,下面代码显示了一张网络图片。
-
with支持传入一下对象,我们在使用过程中尽量要传入activity、fragment因为glide会依赖它们的生命周期,如果onPaush时候,Glide就会暂停加载,重新onResume之后,又会继续加载。
image
-
load() 支持网络图片、二进制流、drawable资源、本地图片的传入。
-
crossFade 这是开启显示淡入淡出的动画
-
override 如果获取的网络图片过大,我们通过它进行一个大小的裁剪,传入width和height参数进行宽高裁剪。
-
diskCacheStrategy 磁盘缓存的设置,默认Glide会开启的。
DiskCacheStrategy.NONE 什么都不缓存
DiskCacheStrategy.SOURCE 只缓存全尺寸图
DiskCacheStrategy.RESULT 只缓存最终的加载图
DiskCacheStrategy.ALL 缓存所有版本图(默认行为)
Glide 不仅缓存了全尺寸的图,还会根据 ImageView 大小所生成的图也会缓存起来。比如,请求一个 800x600 的图加载到一个 400x300 的 ImageView 中,Glide默认会将这原图还有加载到 ImageView 中的 400x300 的图也会缓存起来。
- error 这里的设置是当加载图片出现错误时,显示的图片。
- placeholder 图片加载完成之前显示的占位图。
String url = "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2912495429,3557331556&fm=27&gp=0.jpg";
Glide
.with(this)
.load(url)
.crossFade()
.override(200, 200)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.error(R.mipmap.active_user_default_icon)
.placeholder(R.mipmap.ic_album_image_source_pick)
.into(img);
获取Glide请求完成的Bitmap
通过前面这种方式我们能很轻松的显示一张,图片到ImgeView上。但是我们有时候想获取加载完成后的Bitmap怎么办呢,可以通过SimpleTarget来获取。
//SimpleTarget 获取bitmap,定义siez
SimpleTarget<Bitmap> mSimpleTarget = new SimpleTarget<Bitmap>(200, 200) {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
img.setImageBitmap(resource);
}
};
//获取Bitmap的加载方法。
Glide.with(getApplicationContext())
.load(url)
.asBitmap()
.into(mSimpleTarget);
我们定义了一个SimpleTarget类,里面有一个onResourceReady方法的回调,会返回加载完成的Bitmap对象,into传入这个SimpleTarget对象。
自定义控件使用Glide显示Image
有时候我们自定义的控件也需要显示Imaeg,但是这个控件不是Image类或者子类,那么前面这种方法就会不使用了,我们可以通过ViewTarget来实现这个逻辑。
public class MyImageView extends FrameLayout {
private ImageView mImageView;
public MyImageView(@NonNull Context context) {
super(context);
}
public MyImageView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyImageView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mImageView = new ImageView(getContext());
addView(mImageView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
public void setImage(Drawable drawable) {
mImageView.setImageDrawable(drawable);
}
}
MyImageView img3;
//ViewTarget自定义View也支持获取Bitmap
ViewTarget viewTarget = new ViewTarget<MyImageView, GlideDrawable>(img3) {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
img3.setImage(resource);
}
};
Glide.with(getApplicationContext())
.load(url)
.into(viewTarget);
通过定义一个ViewTarget传入了二个泛型类,一个是MyImageView,一个是GlideDrawable,传入了img3。通过onResourceReady的回调我们可以对自定义控件进行图片显示了。
3.Glide原理了解?
Glide的with、load、into虽然只有三个方法调用但是内部的逻辑是很复杂的,成吨的代码这里我只做一个大概了解。首先with方法会构造一个单例的RequestManager对象。
with方法
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
public static RequestManagerRetriever get() {
return INSTANCE;
}
可以看出来我们一个程序生命周期内只有一个RequestManager对象,工程师在设计的时候很考虑到性能的问题。
load方法
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
接着通过RequestManage对象的load方法返回一个DrawableTypeRequest类,它是一个DrawableRequestBuilder的一个子类,通过类名可以看出来主要作用是主要是构建请求中的一些参数用的,比如我们之前写的 override、error、placeholder、diskCacheStrategy这些辅助方法的设置。
into方法
最后的一个方法也是最复杂的一个,是真正请求网络请求的地方。
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
...
return into(glide.buildImageViewTarget(view, transcodeClass));
}
@SuppressWarnings("unchecked")
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)");
}
}
into接收一个ViewTeger,
into的调用
public <Y extends Target<TranscodeType>> Y into(Y target) {
...
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);
return target;
}
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
runRequest方法来控制请求的队列,如果当前视图状态是Paused状态就会把request添加到等待请求的队列,如果不是则直接执行。里面会有一个HttpUrlFetcher来执行前面封装好的网络请求
4.Glide是如何缓存的?
glide缓存主要分为
- 内存缓存
内存缓存主要是防止同样的图片重复读取到内存中来,解约JVM的内存空间
glide的内存缓存机制是使用LruCache算法实现,首先需要会需要一个key,这个key是通过url+构建请求时候的参数决定的。keyFactory.buildKey的代码就是生成Key的逻辑。glide内存缓存会调用二个方法来获取缓存。
-
loadFromCache
使用LruCache -
loadFromActiveResources
使用弱引用
内存缓存的逻辑就是,先会在loadFromCache中去取缓存,这个是一个LruCaChe算啊实现的,如果取到了值就会把它存入一个弱引用中。这样的防止被LruCache算法回收掉,如果LruCache没有取到就会去loadFromActiveResources方法里面取。
如果都没有那就会开启子线程去网络请求图片。
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
- 磁盘缓存
磁盘缓存主要是防止同一张网络图片,重复从网络中读取和下载,磁盘缓存也是使用LruCache算法实现的。
磁盘缓存有一个逻辑,当需要去加载一张图片的时候,Glide默认不会显示原始图片,而是会对图片进行压缩转换。经过这些转换操作之后才会把,图片显示出来,磁盘缓存默认就是缓存转换后的图片。
Glide
Glide的缓存策略是如果加载是一张1000 * 1000大小的图片,但是最终显示到ImageView上的只有500 * 500,Glide只会缓存最终显示的那张500*500的图片。
Glide的四种缓存策略
- DiskCacheStrategy.NONE 不缓存文件
- DiskCacheStrategy.SOURCE 只缓存原图
- DiskCacheStrategy.RESULT 只缓存最终加载的图(默认的缓存策略)
- DiskCacheStrategy.ALL 同时缓存原图和结果图
配置缓存策略
通过实现GlideModel接口来定义一个自定义的缓存策略类
public class DiskCachMoudle implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
}
@Override
public void registerComponents(Context context, Glide glide) {
}
}
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".test.Activity2">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="com.jin.mak.DiskCachMoudle"
android:value="DiskCachMoudle" />
</application>
Glide源码里面的get方法会自动读取我们在ManifestParert中配置的那个DiskCachMoudle配置。
public static Glide get(Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
Context applicationContext = context.getApplicationContext();
//第一步
List<GlideModule> modules = new ManifestParser(applicationContext).parse();
//第二步
GlideBuilder builder = new GlideBuilder(applicationContext);
for (GlideModule module : modules) {
//在builder构造出glide之前,读取使用者自定义的配置.
module.applyOptions(applicationContext, builder);
}
glide = builder.createGlide();
//第三步
for (GlideModule module : modules) {
module.registerComponents(applicationContext, glide);
}
}
}
}
return glide;
}
applyOptions 方法
-
builder.setBitmapPool ()
这个是用来设置BitMap缓存池的,需要实现一个BitmapPool接口,它的默认实现是LruBitmapPool类。 -
builder. setMemoryCache()
设置内存缓存,需要实现MemoryCache接口,它的默认实现是LruResourceCache类。 -
setResizeService(ExecutorService service)
当资源不在缓存中时,需要通过这个Executor发起请求,默认是实现是FifoPriorityThreadPoolExecutor。 -
setDiskCacheService(ExecutorService service)
读取磁盘缓存的服务,默认实现是FifoPriorityThreadPoolExecutor。 -
setDecodeFormat(DecodeFormat decodeFormat)
用于控制Bitmap解码的清晰度,DecodeFormat可选的值有PREFER_ARGB_8888/PREFER_RGB_565,默认为PREFER_RGB_565。