什么是Glide
Glide是一个快速高效的图片加载库(Android),性能高,流式API的语法特点。
特点
- 1.可配置高,自适应度高;
- 2.支持多种数据源,本地,网络,assets,gif再glide中都是支持的;
- 3.高效缓存,支持memory和disk图片缓存,默认使用二级缓存;
- 4.具有和AF同步的生命周期;
- 5.高效处理Bitmap:使用Bitmap pool复用Bitmap,享元设计模式;
- 6.图片接在过程可以监听。
性能
Glide 充分考虑了Android图片加载性能的两个关键方面: - 图片解码速度
- 解码图片带来的资源压力
为了让用户拥有良好的App使用体验,图片不仅要快速加载,而且还不能因为过多的主线程I/O或频繁的垃圾回收导致页面的闪烁和抖动现象。
Glide使用了多个步骤来确保在Android上加载图片尽可能的快速和平滑:
- 自动、智能地下采样(downsampling)和缓存(caching),以最小化存储开销和解码次数;
- 积极的资源重用,例如字节数组和Bitmap,以最小化昂贵的垃圾回收和堆碎片影响;
- 深度的生命周期集成,以确保仅优先处理活跃的Fragment和Activity的请求,并有利于应用在必要时释放资源以避免在后台时被杀掉。
架构
图片请求,是发送到一个队列里,然后多个线程进行消费。如下图:使用
依赖
1
2
3
4
5
6
7
8
9repositories {
google()
mavenCentral()
}
dependencies {
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}
加载
this是imageview绑定的生命周期对象(Activity或Fragment)。
1 | RequestOptions requestOptions = new RequestOptions(); |
override,默认使用xml设置,如果这里设置,就使用override设置大小。也可以通过RequestOptions设置。
取消加载:
1 | Glide.with(this).clear(imageView); |
尽管及时取消不必要的加载是很好的实践,但这并不是必须的操作。实际上,当 Glide.with() 中传入的 Activity 或 Fragment 实例销毁时,Glide 会自动取消加载并回收资源。
过度
再加载图片过程中,可以定义glide如何从占位到新加载的图片,或从缩略图到全尺寸图像的过度。再这些过程中,可以设置过渡动画。Glide默认实现了交叉淡入效果。
1 | DrawableCrossFadeFactory fadeFactory = new DrawableCrossFadeFactory.Builder().setCrossFadeEnabled(true).build(); |
不幸的是,虽然禁用交叉淡入通常是一个比较好的默认行为,当待加载的图片包含透明像素时仍然可能造成问题。当占位符比实际加载的图片要大,或者图片部分为透明时,禁用交叉淡入会导致动画完成后占位符在图片后面仍然可见。如果你在加载透明图片时使用了占位符,你可以启用交叉淡入,具体办法是调整 DrawableCrossFadeFactory 里的参数并将结果传到 transition() 中:
为了提升性能,请在使用 Glide 向 ListView , GridView, 或 RecyclerView 加载图片时考虑避免使用动画,尤其是大多数情况下,你希望图片被尽快缓存和加载的时候。
变换
在Glide中,Transformations 可以获取资源并修改它,然后返回被修改后的资源。通常变换操作是用来完成剪裁或对位图应用过滤器,但它也可以用于转换GIF动画,甚至自定义的资源类型。
Glide 提供了很多内置的变换,包括:
- CenterCrop
- FitCenter
- CircleCrop
1
2
3
4
5
6
7
8
9
10
11
12
13
14Glide.with(fragment)
.load(url)
.fitCenter()
.into(imageView);
或使用 RequestOptions :
RequestOptions options = new RequestOptions();
options.centerCrop();
Glide.with(fragment)
.load(url)
.apply(options)
.into(imageView);
这里的变换指的是多重变换:默认情况下,每个 transform() 调用,或任何特定转换方法(fitCenter(), centerCrop(), bitmapTransform() etc)的调用都会替换掉之前的变换。
如果你想在单次加载中应用多个变换,请使用 MultiTransformation 类,或其快捷方法 .transforms() 。
比如:
- 1.CircleCrop:圆角
- 2.RoundedCorners:内个叫统一指定
- 3.GranularRoundedCorners:四个角单独指定
- 4.Rotate:旋转
看下图效果:
其他效果,自行调试。
其中变换还有定制变换,重用变变换,具体自行看文档。Generated API
添加 Glide 注解处理器的依赖:1
2
3dependencies {
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}
在 Application 模块中包含一个 AppGlideModule 的实现:
1 | import com.bumptech.glide.annotation.GlideModule; |
Generated API 默认名为 GlideApp ,与 Application 模块中 AppGlideModule的子类包名相同。在 Application 模块中将 Glide.with() 替换为 GlideApp.with(),即可使用该 API 去完成加载工作:
1 | GlideApp.with(fragment) |
@GlideExtension
public class MyAppExtension {
// Size of mini thumb in pixels.
private static final int MINI_THUMB_SIZE = 100;
private MyAppExtension() { } // utility class
//静态方法,BaseRequestOptions的扩展,封装了一些options的配置方法。
@NonNull
@GlideOption
public static BaseRequestOptions> miniThumb(BaseRequestOptions> options) {
return options
.fitCenter()
.override(MINI_THUMB_SIZE);
}
1 | 之后你就可以使用生成的 GlideApp 类调用你的自定义方法: |
GlideApp.with(fragment)
.load(url)
.miniThumb(thumbnailSize)
.into(imageView);
1 | ## 源码分析 |
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
1 | 最总会进入这个方法:这里会给Glide添加生命周期。 |
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
//如果不是主线程,则没有生命周期
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
//主线程,添加了一个空白的Fragment,通过空白的Fragment监听this的生命周期。
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, /parentHint=/ null, isActivityVisible(activity));
}
}
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
1 |
|
registry
.append(Uri.class, InputStream.class, new UriLoader.StreamFactory(contentResolver))
.append(
Uri.class,
ParcelFileDescriptor.class,
new UriLoader.FileDescriptorFactory(contentResolver))
.append(
Uri.class,
AssetFileDescriptor.class,
new UriLoader.AssetFileDescriptorFactory(contentResolver))
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
.append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
.append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
.append(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
.append(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
.append(Uri.class, Uri.class, UnitModelLoader.Factory.
.append(Drawable.class, Drawable.class, UnitModelLoader.Factory.
.append(Drawable.class, Drawable.class, new UnitDrawableDecoder())
/* Transcoders */
.register(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources))
.register(Bitmap.class, byte[].class, bitmapBytesTranscoder)
.register(
Drawable.class,
byte[].class,
new DrawableBytesTranscoder(
bitmapPool, bitmapBytesTranscoder, gifDrawableBytesTranscoder))
.register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);
1 |
|
public RequestBuilder
return asDrawable().load(string);
}
private RequestBuilder
this.model = model;
isModelSet = true;
return this;
}
1 |
|
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don’t retain the transformation applied based on the previous
// View’s scale type.
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
//得到ImageViewTarget
glideContext.buildImageViewTarget(view, transcodeClass),
/targetListener=/ null,
requestOptions,
Executors.mainThreadExecutor());
}
private <Y extends Target
@NonNull Y target,
@Nullable RequestListener
BaseRequestOptions<?> options,
Executor callbackExecutor) {
。。。。。。。。。。。。。。。。。。
// 构建一个请求,实现类SingleRequest
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
//如果上一个请求没有请求完
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
1 | RequestTracker: |
//正在运行的队列
private final Set
Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
//等待中的队列
private final List
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, “Paused, delaying request”);
}
pendingRequests.add(request);
}
}
1 | SingleRequest.begin: |
@Override
public void begin() {
synchronized (requestLock) {
。。。。。。。。。。。。
//正在执行,直接返回异常
if (status == Status.RUNNING) {
throw new IllegalArgumentException(“Cannot restart a running request”);
}
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
…………………………………
}
// 传进宽高
public void onSizeReady(int width, int height) {
synchronized (requestLock) {
………………………………..
loadStatus =
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
}
}
1 | Engine.load |
public
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class> resourceClass,
Class
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
//生成的 key 会用于缓存
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
// 从缓存中拿
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
return waitForExistingOrStartNewJob(
glideContext,
model,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
options,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache,
cb,
callbackExecutor,
key,
startTime);
}
}
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
1 | loadFromMemory从缓存取,也称运行时缓存(如果app被kill掉,就没有了): |
private EngineResource> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
// 先从活动缓存(用户正在显示的图片)拿,一级缓存
EngineResource> active = loadFromActiveResources(key);
if (active != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey(“Loaded resource from active resources”, startTime, key);
}
return active;
}
// 再从内存缓存(活动缓存回收后给到内存缓存)拿,二级缓存
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey(“Loaded resource from cache”, startTime, key);
}
return cached;
}
return null;
}
1 | waitForExistingOrStartNewJob:缓存中没有 |
private
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class> resourceClass,
Class
if (current != null) {
current.addCallback(cb, callbackExecutor);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey(“Added to existing load”, startTime, key);
}
return new LoadStatus(cb, current);
}
// 网络请求了
EngineJob
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
//把decodeJob,添加给引擎job,然后开始执行.
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
1 | EngineJob,引擎Job,这里会由很多的线程池Executor,用来执行操作的: |
private final StateVerifier stateVerifier = StateVerifier.newInstance();
private final ResourceListener resourceListener;
private final Pools.Pool<EngineJob<?>> pool;
private final EngineResourceFactory engineResourceFactory;
private final EngineJobListener engineJobListener;
private final GlideExecutor diskCacheExecutor;
private final GlideExecutor sourceExecutor;
private final GlideExecutor sourceUnlimitedExecutor;
private final GlideExecutor animationExecutor;
private final AtomicInteger pendingCallbacks = new AtomicInteger();
public synchronized void start(DecodeJob
this.decodeJob = decodeJob;
//线程池
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
//执行runnable
executor.execute(decodeJob);
}
1 | DecodeJob.run > runWrapped > runGenerators |
public void run() {
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (CallbackException e) {
}
}
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException(“Unrecognized run reason: “ + runReason);
}
}
//如果你配置了缓存策略,就使用,如果没有,就使用默认SOURCE策略
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException(“Unrecognized stage: “ + stage);
}
}
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
1 | SourceGenerator.startNext |
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
…………………………..
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
//获取网络请求的对象loadData.HttpUrlFetcher
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
//开始网络请求
startNextLoad(loadData);
}
}
return started;
}
// 这里创建了网络请求成功后的callback:DataCacheGenerator
private void cacheData(Object dataToCache) {
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
//网络请求fetcher.loadData
private void startNextLoad(final LoadData<?> toStart) {
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback
@Override
public void onLoadFailed(@NonNull Exception e) {
if (isCurrentRequest(toStart)) {
onLoadFailedInternal(toStart, e);
}
}
});
}
1 | DecodeHelper.getLoadData > HttpGlideUrlLoader.buildLoadData |
List<LoadData>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
//buildLoadData这个函数就返回了HttpUrlFetcher,而HttpUrlFetcher是封装再LoadData对象里的.
public LoadData
@NonNull GlideUrl model, int width, int height, @NonNull Options options) {
// GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
// spent parsing urls.
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
int timeout = options.get(TIMEOUT);
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
1 | 开始请求网络:HttpUrlFetcher.loadData > loadDataWithRedirects |
@Override
public void loadData(
@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
//返回InputStream
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
//把返回的InputStream返回给callback:DataCacheGenerator
callback.onDataReady(result);
}
}
//真正的网络请求再这里,返回InputStream
private InputStream loadDataWithRedirects(
URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException(“Too many (> “ + MAXIMUM_REDIRECTS + “) redirects!”);
} else {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new HttpException(“In re-direct loop”);
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
//url:图片的网络地址
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Stop the urlConnection instance of HttpUrlConnection from following redirects so that
// redirects will be handled by recursive calls to this method, loadDataWithRedirects.
urlConnection.setInstanceFollowRedirects(false);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
// Closing the stream specifically is required to avoid leaking ResponseBodys in addition
// to disconnecting the url connection below. See #2352.
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
}
}
1 |
|
public void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
}
1 | FetcherReadyCallback.onDataFetcherReady > DecodeJob.onDataFetcherReady > decodeFromRetrievedData > decodeFromData > decodeFromFetcher >runLoadPath > path.load LoadPth > LoadPth.loadWithExceptionList > path.decode DecodePath > DecodePath.decode |
//开始解码了
public Resource
DataRewinder
int width,
int height,
@NonNull Options options,
DecodeCallback
throws GlideException {
Resource
Resource
return transcoder.transcode(transformed, options);
}
1 | DecodePath.decodeResource > decodeResourceWithList > decoder.decode StreamBitmapDecoder.decode |
public Resource
@NonNull InputStream source, int width, int height, @NonNull Options options)
throws IOException {
// Use to fix the mark limit to avoid allocating buffers that fit entire images.
final RecyclableBufferedInputStream bufferedStream;
final boolean ownsBufferedStream;
if (source instanceof RecyclableBufferedInputStream) {
bufferedStream = (RecyclableBufferedInputStream) source;
ownsBufferedStream = false;
} else {
bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
ownsBufferedStream = true;
}
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
try {
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
}
}
1 | 最终返回到DecodePath,因为我们是再DecodePath.decode的decodeResource里进来的: |
public Resource
DataRewinder
int width,
int height,
@NonNull Options options,
DecodeCallback
throws GlideException {
Resource
//这里把获取的图片返回到DecodeJob里:
Resource
//其实这里还会对图片进行处理优化
return transcoder.transcode(transformed, options);
}
public Resource
return DecodeJob.this.onResourceDecoded(dataSource, decoded);
}
1 | DecodeJob.onResourceDecoded....> notifyEncodeAndRelease > notifyComplete |
private EngineResource> loadFromActiveResources(Key key) {
EngineResource> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
1 | 接着再通过onResourceReady回调返回到SingleRequest里:这里有个target,这个target,就是我们再into开始时根据imageview创建的ImageViewTarget |
private void onResourceReady(Resource
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
}
notifyLoadSuccess();
}
1 | ImageViewTarget,最终显示图片,DrawableImageViewTarget |
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
private void setResourceInternal(@Nullable Z resource) {
// Order matters here. Set the resource first to make sure that the Drawable has a valid and
// non-null Callback before starting it.
setResource(resource);
maybeUpdateAnimatable(resource);
}
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
1 | ## 生命周期分析 |
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
//空白的Fragment
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
//给到 RequestManager
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
1 | SupportRequestManagerFragment |
@Override
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
unregisterFragmentWithRoot();
}
// ActivityFragmentLifecycle implements Lifecycle
// interface LifecycleListener 生命周期的监听接口
void onDestroy() {
isDestroyed = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onDestroy();
}
}
1 | 所以当界面生命周期发生变化后,实现了LifecycleListener 接口的类,都会收到生命周期的回调监听.我们就可以针对具体的生命周期,执行对应逻辑处理. |
缓存机制
缓存获取流程
先从活动缓存取,有,直接显示,没有从LRU内存缓存取,并LRU内存缓存的数据放入到活动缓存,并显示.如果LRU内存缓存没有,则从LRU磁盘缓存取,放入到活动缓存(磁盘缓存不移除)并显示,都没有,使用http请求下载或外部资源.下载完成缓存到磁盘缓存.
反向同理:生命周期onDestroy时,把活动缓存的移到LRU内存缓存.而下载的图片首先会存到磁盘缓存.
内存缓存大小:自动计算当前内存的1/8.
为什么要一个活动缓存?
如果图片再LRU内存缓存里,而正在显示的图片正在使用,而当有图片添加到LRU内存缓存后,有可能这个LRU内存缓存里的图片被清除了,这个时候就可能出问题了.
LRU Cache
其实就是一个LinkedHashMap,如果参数位true,就会执行LRU算法.数据put进,如果最终数据大于了maxSize,则就会把最少使用,最先添加的的数据清除,如果没有大于maxSize,添加的而是map中已经有的数据,则会把这条数据提到队尾,这再添加一个,就会把队首的数据删掉.
1 | public class LruCache<K, V> { |
参考资料
Ursprünglicher Link: http://nunu03.github.io/2022/06/08/Glide图片加载框架原理/
Copyright-Erklärung: 转载请注明出处.