前言
成為一名優秀的Android開發,需要一份完備的知識體系,在這裡,讓我們一起成長為自己所想的那樣~。
tips:文章太長可以先點贊收藏哦,後續再慢慢閱讀~
複製程式碼
前兩篇我們詳細地分析了Android的網路底層框架OKHttp和封裝框架Retrofit的核心原始碼,如果對OKHttp或Retrofit內部機制不瞭解的可以看看Android主流三方庫原始碼分析(一、深入理解OKHttp原始碼)和Android主流三方庫原始碼分析(二、深入理解Retrofit原始碼)。本篇,我們將會來深入地分析下目前Android使用最廣泛的圖片載入框架框架Glide的原始碼載入流程。
一、基本使用流程
Glide最基本的使用流程就是下面這行程式碼,其它所有擴充套件的額外功能都是以其建造者鏈式呼叫的基礎上增加的。
GlideApp.with(context).load(url).into(iv);
複製程式碼
其中的GlideApp是註解處理器自動生成的,要使用GlideApp,必須先配置應用的AppGlideModule模組,裡面可以為空配置,也可以根據實際情況新增指定配置。
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// 實際使用中根據情況可以新增如下配置
<!--builder.setDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_RGB_565));-->
<!--int memoryCacheSizeBytes = 1024 * 1024 * 20;-->
<!--builder.setMemoryCache(new LruResourceCache(memoryCacheSizeBytes));-->
<!--int bitmapPoolSizeBytes = 1024 * 1024 * 30;-->
<!--builder.setBitmapPool(new LruBitmapPool(bitmapPoolSizeBytes));-->
<!--int diskCacheSizeBytes = 1024 * 1024 * 100;-->
<!--builder.setDiskCache(new InternalCacheDiskCacheFactory(context, diskCacheSizeBytes));-->
}
}
複製程式碼
接下來,本文將針對Glide的最新原始碼版本V4.8.0對Glide載入網路圖片的流程進行詳細地分析與講解,力爭做到讓讀者朋友們知其然也知其所以然。
二、GlideApp.with(context)原始碼詳解
首先,用艽野塵夢繪製的這份Glide框架圖讓我們對Glide的總體框架有一個初步的瞭解。
從GlideApp.with這行程式碼開始,內部主線執行流程如下。
1、GlideApp#with
return (GlideRequests) Glide.with(context);
複製程式碼
2、Glide#with
return getRetriever(context).get(context);
return Glide.get(context).getRequestManagerRetriever();
// 外部使用了雙重檢鎖的同步方式確保同一時刻只執一次Glide的初始化
checkAndInitializeGlide(context);
initializeGlide(context);
// 最終執行到Glide的另一個過載方法
initializeGlide(context, new GlideBuilder());
@SuppressWarnings("deprecation")
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
Context applicationContext = context.getApplicationContext();
// 1、獲取前面應用中帶註解的GlideModule
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
// 2、如果GlideModule為空或者可配置manifest裡面的標誌為true,則獲取manifest裡面
// 配置的GlideModule模組(manifestModules)。
List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled( )) {
manifestModules = new ManifestParser(applicationContext).parse();
}
...
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManag erFactory() : null;
builder.setRequestManagerFactory(factory);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicatio nContext, builder);
}
// 3、初始化各種配置資訊
Glide glide = builder.build(applicationContext);
// 4、把manifestModules以及annotationGeneratedModule裡面的配置資訊放到builder
// 裡面(applyOptions)替換glide預設元件(registerComponents)
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.registerComponents(applicationContext, glide, glide.registry);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(appl icationContext, glide, glide.registry);
}
applicationContext.registerComponentCallbacks(glide );
Glide.glide = glide;
}
複製程式碼
3、GlideBuilder#build
@NonNull
Glide build(@NonNull Context context) {
// 建立請求圖片執行緒池sourceExecutor
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
// 建立硬碟快取執行緒池diskCacheExecutor
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
// 建立動畫執行緒池animationExecutor
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
if (bitmapPool == null) {
// 依據裝置的螢幕密度和尺寸設定各種pool的size
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
// 建立圖片執行緒池LruBitmapPool,快取所有被釋放的bitmap
// 快取策略在API大於19時,為SizeConfigStrategy,小於為AttributeStrategy。
// 其中SizeConfigStrategy是以bitmap的size和config為key,value為bitmap的HashMap
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
// 建立物件陣列快取池LruArrayPool,預設4M
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSiz eInBytes());
}
// 建立LruResourceCache,記憶體快取
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCa cheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
// 建立任務和資源管理引擎(執行緒池,記憶體快取和硬碟快取物件)
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor( ),
GlideExecutor.newAnimationExecutor(),
isActiveResourceRetentionAllowed);
}
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptions.lock(),
defaultTransitionOptions);
}
複製程式碼
4、Glide#Glide構造方法
Glide(...) {
...
// 註冊管理任務執行物件的類(Registry)
// Registry是一個工廠,而其中所有註冊的物件都是一個工廠員工,當任務分發時,
// 根據當前任務的性質,分發給相應員工進行處理
registry = new Registry();
...
// 這裡大概有60餘次的append或register員工元件(解析器、編解碼器、工廠類、轉碼類等等元件)
registry
.append(ByteBuffer.class, new ByteBufferEncoder())
.append(InputStream.class, new StreamEncoder(arrayPool))
// 根據給定子類產出對應型別的target(BitmapImageViewTarget / DrawableImageViewTarget)
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
glideContext =
new GlideContext(
context,
arrayPool,
registry,
imageViewTargetFactory,
defaultRequestOptions,
defaultTransitionOptions,
engine,
logLevel);
}
複製程式碼
5、RequestManagerRetriever#get
@NonNull
public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
// 如果當前執行緒是主執行緒且context不是Application走相應的get過載方法
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());
}
}
// 否則直接將請求與ApplicationLifecycle關聯
return getApplicationManager(context);
}
複製程式碼
這裡總結一下,對於當前傳入的context是application或當前執行緒是子執行緒時,請求的生命週期和ApplicationLifecycle關聯,否則,context是FragmentActivity或Fragment時,在當前元件新增一個SupportFragment(SupportRequestManagerFragment),context是Activity時,在當前元件新增一個Fragment(RequestManagerFragment)。
6、GlideApp#with小結
1、初始化各式各樣的配置資訊(包括快取,請求執行緒池,大小,圖片格式等等)以及glide物件。
2、將glide請求和application/SupportFragment/Fragment的生命週期繫結在一塊。
這裡我們再回顧一下with方法的執行流程。
三、load(url)原始碼詳解
1、GlideRequest(RequestManager)#load
return (GlideRequest<Drawable>) super.load(string);
return asDrawable().load(string);
// 1、asDrawable部分
return (GlideRequest<Drawable>) super.asDrawable();
return as(Drawable.class);
// 最終返回了一個GlideRequest(RequestManager的子類)
return new GlideRequest<>(glide, this, resourceClass, context);
// 2、load部分
return (GlideRequest<TranscodeType>) super.load(string);
return loadGeneric(string);
@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
// model則為設定的url
this.model = model;
// 記錄url已設定
isModelSet = true;
return this;
}
複製程式碼
可以看到,load這部分的原始碼很簡單,就是給GlideRequest(RequestManager)設定了要請求的mode(url),並記錄了url已設定的狀態。
這裡,我們再看看load方法的執行流程。
四、into(iv)原始碼詳解
前方預警,真正複雜的地方開始了。
1、RequestBuilder.into
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
RequestOptions requestOptions = this.requestOptions;
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()) {
// 這個RequestOptions裡儲存了要設定的scaleType,Glide自身封裝了CenterCrop、CenterInside、
// FitCenter、CenterInside四種規格。
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.
}
}
// 注意,這個transcodeClass是指的drawable或bitmap
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}
複製程式碼
2、GlideContext#buildImageViewTarget
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
複製程式碼
3、ImageViewTargetFactory#buildTarget
@NonNull
@SuppressWarnings("unchecked")
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
@NonNull Class<Z> clazz) {
// 返回展示Bimtap/Drawable資源的目標物件
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
複製程式碼
可以看到,Glide內部只維護了兩種target,一種是BitmapImageViewTarget,另一種則是DrawableImageViewTarget,接下來繼續深入。
4、RequestBuilder#into
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
@NonNull RequestOptions options) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
options = options.autoClone();
// 分析1.建立請求
Request request = buildRequest(target, targetListener, options);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousReques t(options, previous)) {
request.recycle();
// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
if (!Preconditions.checkNotNull(previous).isRunni ng()) {
// Use the previous request rather than the new one to allow for optimizations like skipping
// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
// that are done in the individual Request.
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
// 分析2.真正追蹤請求的地方
requestManager.track(target, request);
return target;
}
// 分析1
private Request buildRequest(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
RequestOptions requestOptions) {
return buildRequestRecursive(
target,
targetListener,
/*parentCoordinator=*/ null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions);
}
// 分析1
private Request buildRequestRecursive(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
RequestOptions requestOptions) {
// Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
ErrorRequestCoordinator errorRequestCoordinator = null;
if (errorBuilder != null) {
// 建立errorRequestCoordinator(異常處理物件)
errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
parentCoordinator = errorRequestCoordinator;
}
// 遞迴建立縮圖請求
Request mainRequest =
buildThumbnailRequestRecursive(
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions);
if (errorRequestCoordinator == null) {
return mainRequest;
}
...
Request errorRequest = errorBuilder.buildRequestRecursive(
target,
targetListener,
errorRequestCoordinator,
errorBuilder.transitionOptions,
errorBuilder.requestOptions.getPriority(),
errorOverrideWidth,
errorOverrideHeight,
errorBuilder.requestOptions);
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
return errorRequestCoordinator;
}
// 分析1
private Request buildThumbnailRequestRecursive(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
RequestOptions requestOptions) {
if (thumbnailBuilder != null) {
// Recursive case: contains a potentially recursive thumbnail request builder.
...
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
// 獲取一個正常請求物件
Request fullRequest =
obtainRequest(
target,
targetListener,
requestOptions,
coordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight);
isThumbnailBuilt = true;
// Recursively generate thumbnail requests.
// 使用遞迴的方式建立一個縮圖請求物件
Request thumbRequest =
thumbnailBuilder.buildRequestRecursive(
target,
targetListener,
coordinator,
thumbTransitionOptions,
thumbPriority,
thumbOverrideWidth,
thumbOverrideHeight,
thumbnailBuilder.requestOptions);
isThumbnailBuilt = false;
// coordinator(ThumbnailRequestCoordinator)是作為兩者的協調者,
// 能夠同時載入縮圖和正常的圖的請求
coordinator.setRequests(fullRequest, thumbRequest);
return coordinator;
} else if (thumbSizeMultiplier != null) {
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
// 當設定了縮略的比例thumbSizeMultiplier(0 ~ 1)時,
// 不需要遞迴建立縮圖請求
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest =
obtainRequest(
target,
targetListener,
requestOptions,
coordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight);
RequestOptions thumbnailOptions = requestOptions.clone()
.sizeMultiplier(thumbSizeMultiplier);
Request thumbnailRequest =
obtainRequest(
target,
targetListener,
thumbnailOptions,
coordinator,
transitionOptions,
getThumbnailPriority(priority),
overrideWidth,
overrideHeight);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
// Base case: no thumbnail.
// 沒有縮圖請求時,直接獲取一個正常圖請求
return obtainRequest(
target,
targetListener,
requestOptions,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight);
}
}
private Request obtainRequest(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
RequestOptions requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight) {
// 最終實際返回的是一個SingleRequest物件(將制定的資源載入進對應的Target
return SingleRequest.obtain(
context,
glideContext,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory());
}
複製程式碼
從上原始碼分析可知,我們在分析1處的buildRequest()方法裡建立了請求,且最多可同時進行縮圖和正常圖的請求,最後,呼叫了requestManager.track(target, request)方法,接著看看track裡面做了什麼。
5、RequestManager#track
// 分析2
void track(@NonNull Target<?> target, @NonNull Request request) {
// 加入一個target目標集合(Set)
targetTracker.track(target);
requestTracker.runRequest(request);
}
複製程式碼
6、RequestTracker#runRequest
/**
* Starts tracking the given request.
*/
// 分析2
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");
}
// 否則清空請求,加入延遲請求佇列(為了對這些請求維持一個強引用,使用了ArrayList實現)
pendingRequests.add(request);
}
}
複製程式碼
7、SingleRequest#begin
// 分析2
@Override
public void begin() {
...
if (model == null) {
...
// model(url)為空,回撥載入失敗
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
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)) {
// 當使用override() API為圖片指定了一個固定的寬高時直接執行onSizeReady,
// 最終的核心處理位於onSizeReady
onSizeReady(overrideWidth, overrideHeight);
} else {
// 根據imageView的寬高算出圖片的寬高,最終也會走到onSizeReady
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
// 預先載入設定的縮圖
target.onLoadStarted(getPlaceholderDrawable());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
複製程式碼
從requestManager.track(target, request)開始,最終會執行到SingleRequest#begin()方法的onSizeReady,可以猜到(因為後面只做了預載入縮圖的處理),真正的請求就是從這裡開始的,我們們進去一探究竟~
8、SingleRequest#onSizeReady
// 分析2
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
...
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
...
// 根據給定的配置進行載入,engine是一個負責載入、管理活躍和快取資源的引擎類
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.getUseUnlimitedSourceGeneratorsP ool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this);
...
}
複製程式碼
終於看到Engine類了,感覺距離成功不遠了,繼續~
9、Engine#load
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb) {
...
// 先從弱引用中查詢,如果有的話回撥onResourceReady並直接返回
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
// 沒有再從記憶體中查詢,有的話會取出並放到ActiveResources(內部維護的弱引用快取map)裡面
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
// 如果記憶體中沒有,則建立engineJob(decodejob的回撥類,管理下載過程以及狀態)
EngineJob<R> 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內部維護的HashMap中
jobs.put(key, engineJob);
// 關注點8 後面分析會用到
// 註冊ResourceCallback介面
engineJob.addCallback(cb);
// 內部開啟執行緒去請求
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
// willDecodeFromCache方法內部根據不同的階段stage,如果是RESOURCE_CACHE/DATA_CACHE則返回true,使用diskCacheExecutor,否則呼叫getActiveSourceExecutor,內部會根據相應的條件返回sourceUnlimitedExecutor/animationExecutor/sourceExecutor
GlideExecutor executor =
decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
複製程式碼
可以看到,最終Engine(引擎)類內部會執行到自身的start方法,它會根據不同的配置採用不同的執行緒池使用diskCacheExecutor/sourceUnlimitedExecutor/animationExecutor/sourceExecutor來執行最終的解碼任務decodeJob。
10、DecodeJob#run
runWrapped();
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
// 關注點1
currentGenerator = getNextGenerator();
// 關注點2 內部會呼叫相應Generator的startNext()
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
// 關注點3 將獲取的資料解碼成對應的資源
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
// 關注點1,完整情況下,會非同步依次生成這裡的ResourceCacheGenerator、DataCacheGenerator和SourceGenerator物件,並在之後執行其中的startNext()
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);
}
}
複製程式碼
11、SourceGenerator#startNext
// 關注點2
@Override
public boolean startNext() {
// dataToCache資料不為空的話快取到硬碟(第一執行該方法是不會呼叫的)
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
// 關注點4 getLoadData()方法內部會在modelLoaders裡面找到ModelLoder物件
// (每個Generator對應一個ModelLoader),
// 並使用modelLoader.buildLoadData方法返回一個loadData列表
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCache able(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDat aClass()))) {
started = true;
// 關注點6 通過loadData物件的fetcher物件(有關注點3的分析可知其實現類為HttpUrlFetcher)的
// loadData方法來獲取圖片資料
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
複製程式碼
12、DecodeHelper#getLoadData
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model) ;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
// 注意:這裡最終是通過HttpGlideUrlLoader的buildLoadData獲取到實際的loadData物件
LoadData<?> current =
modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
複製程式碼
13、HttpGlideUrlLoader#buildLoadData
@Override
public LoadData<InputStream> buildLoadData(@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) {
// 關注點5
modelCache.put(model, 0, 0, model);
url = model;
}
}
int timeout = options.get(TIMEOUT);
// 注意,這裡建立了一個DataFetcher的實現類HttpUrlFetcher
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
// 關注點5
public void put(A model, int width, int height, B value) {
ModelKey<A> key = ModelKey.get(model, width, height);
// 最終是通過LruCache來快取對應的值,key是一個ModelKey物件(由model、width、height三個屬性組成)
cache.put(key, value);
}
複製程式碼
從這裡的分析,我們明白了HttpUrlFetcher實際上就是最終的請求執行者,而且,我們知道了Glide會使用LruCache來對解析後的url來進行快取,以便後續可以省去解析url的時間。
14、HttpUrlFetcher#loadData
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
// 關注點6
// loadDataWithRedirects內部是通過HttpURLConnection網路請求資料
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
// 請求成功回撥onDataReady()
callback.onDataReady(result);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
} finally {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
}
}
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
Map<String, String> headers) throws IOException {
...
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();
// 只要是2xx形式的狀態碼則判斷為成功
if (isHttpOk(statusCode)) {
// 從urlConnection中獲取資源流
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
...
// 重定向請求
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == INVALID_STATUS_CODE) {
throw new HttpException(statusCode);
} else {
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
}
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
throws IOException {
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
int contentLength = urlConnection.getContentLength();
stream = ContentLengthInputStream.obtain(urlConnection.getInputStr eam(), contentLength);
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
}
stream = urlConnection.getInputStream();
}
return stream;
}
複製程式碼
在HttpUrlFetcher#loadData方法的loadDataWithRedirects裡面,Glide通過原生的HttpURLConnection進行請求後,並呼叫getStreamForSuccessfulRequest()方法獲取到了最終的圖片流。
15、DecodeJob#run
在我們通過HtttpUrlFetcher的loadData()方法請求得到對應的流之後,我們還必須對流進行處理得到最終我們想要的資源。這裡我們回到第10步DecodeJob#run方法的關注點3處,這行程式碼將會對流進行解碼。
decodeFromRetrievedData();
複製程式碼
接下來,繼續看看他內部的處理。
private void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Retrieved data", startFetchTime,
"data: " + currentData
+ ", cache key: " + currentSourceKey
+ ", fetcher: " + currentFetcher);
}
Resource<R> resource = null;
try {
// 核心程式碼
// 從資料中解碼得到資源
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
// 關注點8
// 編碼和釋出最終得到的Resource<Bitmap>物件
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
long startTime = LogTime.getLogTime();
// 核心程式碼
// 進一步包裝瞭解碼方法
Resource<R> result = decodeFromFetcher(data, dataSource);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded result " + result, startTime);
}
return result;
} finally {
fetcher.cleanup();
}
}
@SuppressWarnings("unchecked")
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
// 核心程式碼
// 將解碼任務分發給LoadPath
return runLoadPath(data, dataSource, path);
}
private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
LoadPath<Data, ResourceType, R> path) throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
// 將資料進一步包裝
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
// ResourceType in DecodeCallback below is required for compilation to work with gradle.
// 核心程式碼
// 將解碼任務分發給LoadPath
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
複製程式碼
16、LoadPath#load
public Resource<Transcode> load(DataRewinder<Data> rewinder, @NonNull Options options, int width,
int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());
try {
// 核心程式碼
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
} finally {
listPool.release(throwables);
}
複製程式碼
}
private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder,
@NonNull Options options,
int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback,
List<Throwable> exceptions) throws GlideException {
Resource<Transcode> result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
// 核心程式碼
// 將解碼任務又進一步分發給DecodePath的decode方法去解碼
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
複製程式碼
17、DecodePath#decode
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
// 核心程式碼
// 繼續呼叫DecodePath的decodeResource方法去解析出資料
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
@NonNull
private Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder, int width,
int height, @NonNull Options options) throws GlideException {
List<Throwable> exceptions = Preconditions.checkNotNull(listPool.acquire());
try {
// 核心程式碼
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
listPool.release(exceptions);
}
}
@NonNull
private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width,
int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException {
Resource<ResourceType> result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
// 獲取包裝的資料
data = rewinder.rewindAndGet();
// 核心程式碼
// 根據DataType和ResourceType的型別分發給不同的解碼器Decoder
result = decoder.decode(data, width, height, options);
}
} catch (IOException | RuntimeException | OutOfMemoryError e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Failed to decode data for " + decoder, e);
}
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
複製程式碼
可以看到,經過一連串的巢狀呼叫,最終執行到了decoder.decode()這行程式碼,decode是一個ResourceDecoder<DataType, ResourceType>介面(資源解碼器),根據不同的DataType和ResourceType它會有不同的實現類,這裡的實現類是ByteBufferBitmapDecoder,接下來讓我們來看看這個解碼器內部的解碼流程。
18、ByteBufferBitmapDecoder#decode
/**
* Decodes {@link android.graphics.Bitmap Bitmaps} from {@link java.nio.ByteBuffer ByteBuffers}.
*/
public class ByteBufferBitmapDecoder implements ResourceDecoder<ByteBuffer, Bitmap> {
...
@Override
public Resource<Bitmap> decode(@NonNull ByteBuffer source, int width, int height,
@NonNull Options options)
throws IOException {
InputStream is = ByteBufferUtil.toStream(source);
// 核心程式碼
return downsampler.decode(is, width, height, options);
}
}
複製程式碼
可以看到,最終是使用了一個downsampler,它是一個壓縮器,主要是對流進行解碼,壓縮,圓角等處理。
19、DownSampler#decode
public Resource<Bitmap> decode(InputStream is, int outWidth, int outHeight,
Options options) throws IOException {
return decode(is, outWidth, outHeight, options, EMPTY_CALLBACKS);
}
@SuppressWarnings({"resource", "deprecation"})
public Resource<Bitmap> decode(InputStream is, int requestedWidth, int requestedHeight,
Options options, DecodeCallbacks callbacks) throws IOException {
Preconditions.checkArgument(is.markSupported(), "You must provide an InputStream that supports"
+ " mark()");
...
try {
// 核心程式碼
Bitmap result = decodeFromWrappedStreams(is, bitmapFactoryOptions,
downsampleStrategy, decodeFormat, isHardwareConfigAllowed, requestedWidth,
requestedHeight, fixBitmapToRequestedDimensions, callbacks);
// 關注點7
// 解碼得到Bitmap物件後,包裝成BitmapResource物件返回,
// 通過內部的get方法得到Resource<Bitmap>物件
return BitmapResource.obtain(result, bitmapPool);
} finally {
releaseOptions(bitmapFactoryOptions);
byteArrayPool.put(bytesForOptions);
}
}
private Bitmap decodeFromWrappedStreams(InputStream is,
BitmapFactory.Options options, DownsampleStrategy downsampleStrategy,
DecodeFormat decodeFormat, boolean isHardwareConfigAllowed, int requestedWidth,
int requestedHeight, boolean fixBitmapToRequestedDimensions,
DecodeCallbacks callbacks) throws IOException {
// 省去計算壓縮比例等一系列非核心邏輯
...
// 核心程式碼
Bitmap downsampled = decodeStream(is, options, callbacks, bitmapPool);
callbacks.onDecodeComplete(bitmapPool, downsampled);
...
// Bimtap旋轉處理
...
return rotated;
}
private static Bitmap decodeStream(InputStream is, BitmapFactory.Options options,
DecodeCallbacks callbacks, BitmapPool bitmapPool) throws IOException {
...
TransformationUtils.getBitmapDrawableLock().lock();
try {
// 核心程式碼
result = BitmapFactory.decodeStream(is, null, options);
} catch (IllegalArgumentException e) {
...
} finally {
TransformationUtils.getBitmapDrawableLock().unlock();
}
if (options.inJustDecodeBounds) {
is.reset();
}
return result;
}
複製程式碼
從以上原始碼流程我們知道,最後是在DownSampler的decodeStream()方法中使用了BitmapFactory.decodeStream()來得到Bitmap物件。然後,我們來分析下圖片時如何顯示的,我們回到步驟19的DownSampler#decode方法,看到關注點7,這裡是將Bitmap包裝成BitmapResource物件返回,通過內部的get方法可以得到Resource物件,再回到步驟15的DecodeJob#run方法,這是使用了notifyEncodeAndRelease()方法對Resource物件進行了釋出。
20、DecodeJob#notifyEncodeAndRelease
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
...
notifyComplete(result, dataSource);
...
}
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
複製程式碼
從以上EngineJob的原始碼可知,它實現了DecodeJob.CallBack這個介面。
class EngineJob<R> implements DecodeJob.Callback<R>,
Poolable {
...
}
複製程式碼
21、EngineJob#onResourceReady
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
this.resource = resource;
this.dataSource = dataSource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
private static class MainThreadCallback implements Handler.Callback{
...
@Override
public boolean handleMessage(Message message) {
EngineJob<?> job = (EngineJob<?>) message.obj;
switch (message.what) {
case MSG_COMPLETE:
// 核心程式碼
job.handleResultOnMainThread();
break;
...
}
return true;
}
}
複製程式碼
從以上原始碼可知,通過主執行緒Handler物件進行切換執行緒,然後在主執行緒呼叫了handleResultOnMainThread這個方法。
@Synthetic
void handleResultOnMainThread() {
...
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = cbs.size(); i < size; i++) {
ResourceCallback cb = cbs.get(i);
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource, dataSource);
}
}
...
}
複製程式碼
這裡又通過一個迴圈呼叫了所有ResourceCallback的方法,讓我們回到步驟9處Engine#load方法的關注點8這行程式碼,這裡對ResourceCallback進行了註冊,在步驟8出SingleRequest#onSizeReady方法裡的engine.load中,我們看到最後一個引數,傳入的是this,可以明白,engineJob.addCallback(cb)這裡的cb的實現類就是SingleRequest。接下來,讓我們看看SingleRequest的onResourceReady方法。
22、SingleRequest#onResourceReady
/**
* A callback method that should never be invoked directly.
*/
@SuppressWarnings("unchecked")
@Override
public void onResourceReady(Resource<?> resource, DataSource dataSource) {
...
// 從Resource<Bitmap>中得到Bitmap物件
Object received = resource.get();
...
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
private void onResourceReady(Resource<R> resource, R resultDataSource dataSource) {
...
try {
...
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
// 核心程式碼
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
}
複製程式碼
在SingleRequest#onResourceReady方法中又呼叫了target.onResourceReady(result, animation)方法,這裡的target其實就是我們在into方法中建立的那個BitmapImageViewTarget,看到BitmapImageViewTarget類,我們並沒有發現onResourceReady方法,但是我們從它的子類ImageViewTarget中發現了onResourceReady方法,從這裡我們繼續往下看。
23、ImageViewTarget#onResourceReady
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
implements Transition.ViewAdapter {
...
@Override
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 abstract void setResource(@Nullable Z resource);
}
複製程式碼
這裡我們在回到BitmapImageViewTarget的setResource方法中,我們終於看到Bitmap被設定到了當前的imageView上了。
public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {
...
@Override
protected void setResource(Bitmap resource) {
view.setImageBitmap(resource);
}
}
複製程式碼
到這裡,我們的分析就結束了,從以上的分析可知,Glide將大部分的邏輯處理都放在了最後一個into方法中,裡面經過了20多個分析步驟才將請求圖片流、解碼出圖片,到最終設定到對應的imageView上。
最後,這裡給出一份我花費了數個小時繪製的完整Glide載入流程圖,非常珍貴,大家可以仔仔細細再把Glide的主體流程在梳理一遍。
五、總結
到此,Glide整個的載入流程分析就結束了,可以看到,Glide最核心的邏輯都聚集在into()方法中,它裡面的設計精巧而複雜,這部分的原始碼分析非常耗時,但是,如果你真真正正地去一步步去深入其中,你也許在Android進階之路上將會有頓悟的感覺。目前,Android主流三方庫原始碼分析系列已經對網路庫(OkHttp、Retrofit)和圖片載入庫(Glide)進行了詳細的原始碼分析,接下來,將會對資料庫框架GreenDao的核心原始碼進行深入的分析,敬請期待~
參考連結:
1、Glide V4.8.0原始碼
讚賞
如果這個庫對您有很大幫助,您願意支援這個專案的進一步開發和這個專案的持續維護。你可以掃描下面的二維碼,讓我喝一杯咖啡或啤酒。非常感謝您的捐贈。謝謝!
Contanct Me
● 微信:
歡迎關注我的微信:
bcce5360
● 微信群:
微信群如果不能掃碼加入,麻煩大家想進微信群的朋友們,加我微信拉你進群。
● QQ群:
2千人QQ群,Awesome-Android學習交流群,QQ群號:959936182, 歡迎大家加入~
About me
-
Email: chao.qu521@gmail.com
-
Blog: jsonchao.github.io/
-
掘金: juejin.im/user/5a3ba9…