前言
依赖
implementation 'com.github.bumptech.glide:glide:4.16.0'
github: GitHub - bumptech/glide: An image loading and caching library for Android focused on smooth scrolling
基本使用
//加载url
Glide.with(this)
.load(url)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.into(imageView)
//加载本地drawable资源
Glide.with(this)
.load(R.mipmap.ic_launcher)
.into(imageView)
//加载gif,Glide会智能判断
Glide.with(this)
.load(R.drawable.a)
.into(imageView)
//asBitmap只加载静态图片,如果图片为gif则加载第一帧
Glide.with(this)
.asBitmap()
.load(R.drawable.a)
.into(imageView)
//后备回调符
Glide.with(this)
.load(url)
.fallback(R.mipmap.ic_launcher) //当url为null时显示
.into(imageView)
RequestOptions
Glide的配置都可以通过RequestOptions配置,用于提取公共属性,复用。
RequestOptions options = new RequestOptions()
.placeholder(R.drawable.ic_launcher_background)
.error(R.drawable.ic_launcher_foreground)
.fallback(R.mipmap.ic_launcher);
Glide.with(this).load(URL).apply(options).into(view);
设置图片大小
宽高单位是px
Glide.with(this).load(URL).override(100,100).into(view);
设置缩略图
RequestBuilder<Drawable> requestBuilder= Glide.with(this)
.asDrawable().sizeMultiplier(0.5f);//显示原图的50%
Glide.with(this).load(URL).thumbnail(requestBuilder).into(view);
先加载缩略图,再加载原图
//先加载缩略图,再加载原图
RequestBuilder<Drawable> requestBuilder1= Glide.with(this).load(URL1);
Glide.with(this).load(URL).diskCacheStrategy(DiskCacheStrategy.NONE)
.thumbnail(requestBuilder1).into(view);
缓存设置
Glide默认开启内存缓存和硬盘缓存
禁用缓存策略
Glide.with(this).load(URL).skipMemoryCache(true).into(view);
硬盘缓存策略
- DiskCacheStrategy.NONE //不开启硬盘缓存
- DiskCacheStrategy.DATA //只缓存原始图片
- DiskCacheStrategy.RESOURCE //只缓存转换后的图片
- DiskCacheStrategy.ALL //同时缓存原始图片和转换后图片
- DiskCacheStrategy.AUTOMATIC //智能模式,Glide根据图片资源选择模式
Glide.with(this).load(URL).diskCacheStrategy(DiskCacheStrategy.NONE).into(view);
预加载
Glide.with(this).load(URL).preload();
文件下载
new Thread(() -> {
FutureTarget<File> target = Glide.with(MainActivity.this).asFile().load(URL).submit();
try {
File image = target.get();
File file = new File(getCacheDir(),"file.png");
image.compareTo(file);
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
图片裁剪
Glide.with(this)
.load(url)
//.centerCrop() //居中剪裁
//.fitCenter() // 默认
.circleCrop() //圆形图片
.into(imageView)
过渡动画
//过渡动画
Glide.with(this)
.load(URL)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.transition(withCrossFade(1000)) //默认为300ms
.into(view);
可以自定义动画
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromAlpha="0"
android:toAlpha="1" />
Glide.with(this).load(URL).diskCacheStrategy(DiskCacheStrategy.NONE).
transition(GenericTransitionOptions.with(R.anim.anim_alpha)).
into(view)
源码分析
with
RequestManager requestManager = Glide.with(this);
load
RequestBuilder<Drawable> load = requestManager.load(URL);
into
CustomViewTarget<ImageView, Drawable> target = new CustomViewTarget<ImageView, Drawable>(view) {
@Override
protected void onResourceCleared(@Nullable Drawable placeholder) {
Log.e(TAG, "资源清理");
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
Log.e(TAG, "加载失败");
}
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
Log.e(TAG, "加载成功");
}
};
CustomViewTarget<ImageView, Drawable> into = load.into(target);
1:RequestBuilder.java
->into
->return into( glideContext.buildImageViewTarget(view, transcodeClass), /* targetListener= */ null, requestOptions, Executors.mainThreadExecutor()); //构造一个ImageViewTarget
->Request request = buildRequest(target, targetListener, options, callbackExecutor); //构造一个请求,接口实现SingleRequest
2:RequestManager.java
->requestManager.track(target, request);
3:RequestTracker.java
->requestTracker.runRequest(request);//两个set集合添加request请求
->request.begin();
4:SingleRequest.java
->onSizeReady(overrideWidth, overrideHeight);
5:Engine.java
->memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);//三级缓存
->EngineResource<?> active = loadFromActiveResources(key); //一级缓存活动--运行时缓存
->EngineResource<?> cached = loadFromCache(key); //二级缓存Cache--运行时缓存
->waitForExistingOrStartNewJob
->jobs.get(key, onlyRetrieveFromCache)////三级缓存磁盘-非运行时缓存
6:EngineJob.java
如果缓存里都没有,去请求
->engineJob.start(decodeJob);
7:DecodeJob.java
->run()
->runWrapped()
->case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
->currentGenerator.startNext()
8:SourceGenerator.java
->startNext()
9:DecodeHelper.java
->loadData = helper.getLoadData().get(loadDataListIndex++);
-> glideContext.getRegistry().getModelLoaders(model) //这里会取注册的Loaders
10:ModelLoader.java
->LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
11:HttpGlideUrlLoader.java
->buildLoadData
->return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
12:HttpUrlFetcher.java
->loadData
->loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders()); //发起HttpUrlConnect请求
->getStreamForSuccessfulRequest;//获得InputStream
->stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);//会对InputSteam做一系列的优化,压缩,防止过大导致崩溃
……
->InputStream会转成Bitmap
……
最终回调到CustomViewTarget.onResourceReady
也就是
贴上完整的时序图
Glide生命周期
Glide之所以如此灵活,无须对用户的Activity或Fragment进行管理,避免内存泄漏,在于空白的父类SupportRequestManagerFragment,通过jetpack的lifecycle进行管理。
面试总结
1:在子线程使用Glide.with函数,会发生什么?
答:Glide.with在子线程里不会添加生命周期,在主线程才会添加一个FragmentActivity,绑定使用的Activity生命周期
2:项目中大量使用Glide,会造成内存泄漏问题,请问如何避免?
答:通过Glide.with()传入的对象,尽量是包含能自动回收的Activity等包含生命周期的作用域,避免使用Application这种,造成无法回收现象。
3:使用Glide为什么要加入网络权限?
<uses-permission android:name="android.permission.INTERNET" />
答:因为内部会有HttpURLConnection请求,在执行Request事务后,会先从活动缓存取,如果没有,再去内存缓存,再去磁盘缓存,然后通过HttpUrlFetcher发起HttpURLConnection请求,所有需要网络权限
4:Glide源码里面的缓存,为什么要有 活动缓存 还需要 有内存缓存?或者为什么设计三层缓存?
答:简单来说,一级缓存采用LRU算法,最新最少使用的图片,当请求队列有新的图片时候,会淘汰掉最新最少使用的图片,如果这张图片后面还需要页面展示,那么就会有产生效率问题,因此再增加了一层不采用LRU的内存缓存。