配色: 字号:
Glide源码导读
2016-09-06 | 阅:  转:  |  分享 
  
Glide源码导读



最近比较无聊,为了找点事干,就花了两天时间把Glide的源码大概看了一下。刚开始看Glide的源码头脑还是比较乱的,因为作者引入了几个概念,又大量用了泛型,如果不了解这些概念读起代码来就比较痛苦,我也没有详细看各种实现细节的东西,只是了解了下这个框架的大概样子,在这篇文章里,我会介绍下Glide中的一些关键概念,并走一遍图片加载流程,如果你要阅读Glide源码的话,应该多少会有点帮助。



基本概念



首先是三个最基本的概念:Model,Data和Resource。



想一下,我们加载图片需要什么?一般是一个url,但url并不是所有情况,还有资源ID,文件等等,甚至可以是Feed流中的一条Feed,虽然一般我们会从Feed中取出图片的url来转换为从url中加载的情况,Glide把这些抽像为了一个概念,就是Model,所以Model就是数据地址的最初来源。



Model并不能直接解析为图片,比如一个url,是要转换为网络流的InputStream才能被解析为图片的,Model需要进行一次转换才能做为数据解析的数据源,这些转换后的东西就叫做Data,Glide并没有一个Data类,但有很多和它相关的概念,如dataClase,DataFetcher等。



那么Resource呢,其实它就是一个包装类,一个wrapper,它wrap一个对象,使这个对象可以通过对象池进行缓存与重用。



这三个基本概念介绍完了,接下来看一下Glide基本框架。



做为一个图片加载框架,肯定会包含缓存部分。



可以从网上很容易的了解到,Glide的磁盘缓存可以缓存原始数据,也可以缓存处理过的数据。什么意思呢,就是你有一张1000x1000的图片,但你是在列表中展示的,比如是200x200,那么缓存时可以直接将整个网络流缓存下来,即1000x1000的图片,要展示的时候再缩放,但这就降低了展示效率,所以Glide也可以把处理过的200x200的图片缓存起来,增加了缓存大小,但优化了展示速度。



至于怎么把数据缓存到磁盘,就引入了一个叫Encoder的概念,Encoder是用来持久化数据的。



但看源码时你会发现,Glide中有一个类叫Registry,可以注册多个Encoder,但你会发现它还可以注册ResourceEncoder。这两个Encoder很容易引起混淆,而其实ResouseEncoder继承自Encoder。Encoder是用来持久化Data的,ResourceEncoder是用来持久化Resource的。看Glide默认注册的Encoder就知道了,默认注册的Encoder为ByteBuffer和InputStream,而ResourceEncoder是Bitmap、BitmapDrawable和GifDrawable,也就是一个持久化原始数据,一个持久化处理过的数据。我感觉把Encoder做为一个上级的抽象,引入一个和ResourceEncoder同级的DataEncoder就好理解了,正好和前面的基本概念Data和Resource对应。



有Encoder就有Decoder,对应的类叫ResourceDecoder,用来将数据(InputStream等)解析为Resource。



图片加载出来后还可能会应用各种变换,如圆角图片,圆形图片,处理这部分工作的叫Transformation



基础概念介绍的差不多了,加载流程也差不多出来了:



sequence1



但我们发现前面的介绍中少了一环,即:Glide是怎么把Model转换为Data的。这就引入另一个概念,ModelLoader,就是把Model转换成Data的,为了方便说明,直接把这个类的代码贴上来了,去掉了一些注释。



/

Afactoryinterfacefortranslatinganarbitrarilycomplexdatamodelintoaconcretedatatype

thatcanbeusedbyan{@linkDataFetcher}toobtainthedataforaresourcerepresentedbythe

model.



@paramThetypeofthemodel.

@paramThetypeofthedatathatcanbeusedbya

{@linkcom.bumptech.glide.load.ResourceDecoder}todecodearesource.

/

publicinterfaceModelLoader{



/

Containsasetof{@linkcom.bumptech.glide.load.KeyKeys}identifyingthesourceoftheload,

alternatecachekeyspointingtoequivalentdata,anda

{@linkcom.bumptech.glide.load.data.DataFetcher}thatcanbeusedtofetchdatanotfoundin

cache.



@paramThetypeofdatathatwellbeloaded.

/

classLoadData{

publicfinalKeysourceKey;

publicfinalListalternateKeys;

publicfinalDataFetcherfetcher;



publicLoadData(KeysourceKey,DataFetcherfetcher){

this(sourceKey,Collections.emptyList(),fetcher);

}



publicLoadData(KeysourceKey,ListalternateKeys,DataFetcherfetcher){

this.sourceKey=Preconditions.checkNotNull(sourceKey);

this.alternateKeys=Preconditions.checkNotNull(alternateKeys);

this.fetcher=Preconditions.checkNotNull(fetcher);

}

}



LoadDatabuildLoadData(Modelmodel,intwidth,intheight,Optionsoptions);



booleanhandles(Modelmodel);

}

ModelLoader有两个方法,一个handles表示是否可以处理这个类型的Model,如果可以的话就可以通过buildLoadData生成一个LoadData,而LoadData包含了要用来做缓存的key,及用来获取数据的DataFetcher。



到这里,整个加载流程就清楚了:



sequence2



基本加载流程



接下来要做的就是根据我们的使用方法走一遍流程,调用如下:



Glide.with(mContext)

.load(url)

.apply(RequestOptions.placeholderOf(R.drawable.loading))

.into(myImageView);

一步步看,先是Glide.with(mContext):



publicstaticRequestManagerwith(Contextcontext){

RequestManagerRetrieverretriever=RequestManagerRetriever.get();

returnretriever.get(context);

}

通过RequestManagerRetriever获取到了一个RequestManager,至于为什么还需要一个RequestManagerRetriever并有各种重载方法,主要是因为Glide通过SupportRequestManagerFragment和RequestManagerFragment关联了Activity或Fragment的生命周期,用来做pauseRequests等操作。



然后是load:



publicRequestBuilderload(@NullableObjectmodel){

returnasDrawable().load(model);

}



publicRequestBuilderasDrawable(){

returnas(Drawable.class).transition(newDrawableTransitionOptions());

}



publicRequestBuilderas(ClassresourceClass){

returnnewRequestBuilder<>(glide.getGlideContext(),this,resourceClass);

}

是asDrawable.load(model)的缩写,就是说这个Model我是要加载为Drawable的,最终返回一个RequestBuilder,看名字就知道是做什么了,不过这个类主要是设置ThumbnailRequest,Transition等个别设置(旧版本中placeHolder等也是在这里设置的),大部分设置在RequestOptions里,这就是下面这一句:



apply(RequestOptions.placeholderOf(R.drawable.loading))

应用一个RequestOptions,RequestOptions可以设置各种请求相关的选项,如占位图片,加载失败的图片,缓存策略等。RequestOptions继承自BaseRequestOptions,但全是工厂方法生成各种RequestOptions。



最后就是into了,把图片加载到一个Target中。



publicTargetinto(ImageViewview){

...

returninto(context.buildImageViewTarget(view,transcodeClass));

}



public>Yinto(@NonNullYtarget){

Util.assertMainThread();

Preconditions.checkNotNull(target);

if(!isModelSet){

thrownewIllegalArgumentException("Youmustcall#load()beforecalling#into()");

}



Requestprevious=target.getRequest();



if(previous!=null){

requestManager.clear(target);

}



requestOptions.lock();

Requestrequest=buildRequest(target);

target.setRequest(request);

requestManager.track(target,request);



returntarget;

}

Target是要加载到的目标,比如ImageViewTarget,AppWidgetTarget,在这里我们传进来了一个ImageView,内部生成了一个DrawableImageViewTarget。这里最主要的操作是buildRequest然后交给RequestManager去track。



voidtrack(Targettarget,Requestrequest){

targetTracker.track(target);

requestTracker.runRequest(request);

}



//RequestTracker

publicvoidrunRequest(Requestrequest){

requests.add(request);

if(!isPaused){

request.begin();

}else{

pendingRequests.add(request);

}

}

TargetTracker主要就是记录一下所有正在加载的图片的Target,所以加载行为是在RequestTracker.runRequest中的,runRequest先判断是否是pause状态(RequestManager设置),如果不是就直接调用Request.begin触发加载,否则就回到pending队列里等待resume。



除了设置缩略图的情景,使用的Request都是SingleRequest,看一下它的begin方法:



publicvoidbegin(){

stateVerifier.throwIfRecycled();

startTime=LogTime.getLogTime();

if(model==null){

if(Util.isValidDimensions(overrideWidth,overrideHeight)){

width=overrideWidth;

height=overrideHeight;

}

//Onlylogatmoreverboseloglevelsiftheuserhassetafallbackdrawable,because

//fallbackDrawablesindicatetheuserexpectsnullmodelsoccasionally.

intlogLevel=getFallbackDrawable()==null?Log.WARN:Log.DEBUG;

onLoadFailed(newGlideException("Receivednullmodel"),logLevel);

return;

}



status=Status.WAITING_FOR_SIZE;

if(Util.isValidDimensions(overrideWidth,overrideHeight)){

onSizeReady(overrideWidth,overrideHeight);

}else{

target.getSize(this);

}



if((status==Status.RUNNING||status==Status.WAITING_FOR_SIZE)

&&canNotifyStatusChanged()){

target.onLoadStarted(getPlaceholderDrawable());

}

if(Log.isLoggable(TAG,Log.VERBOSE)){

logV("finishedrunmethodin"+LogTime.getElapsedMillis(startTime));

}

}

加载逻辑是这几行:



if(Util.isValidDimensions(overrideWidth,overrideHeight)){

onSizeReady(overrideWidth,overrideHeight);

}else{

target.getSize(this);

}

判断下是否知道Target的大小,如果大小已知就调用onSizeReady,否则就调用target.getSize获取它的大小,当成功获取到大小后,会通过回调继续调用onSizeReady,所以整个加载方法都是在onSizeReady里的。至于Target怎么获取它的大小,那要看它的实现了,对于ImageViewTarget,是通过ViewTreeObserver.OnPreDrawListener等到View要测绘的时候就知道它的大小了。



onSizeReady就是把操作转移到了Engine.load



publicLoadStatusload(

GlideContextglideContext,

Objectmodel,

Keysignature,

intwidth,

intheight,

ClassresourceClass,

ClasstranscodeClass,

Prioritypriority,

DiskCacheStrategydiskCacheStrategy,

Map,Transformation>transformations,

booleanisTransformationRequired,

Optionsoptions,

booleanisMemoryCacheable,

booleanuseUnlimitedSourceExecutorPool,

ResourceCallbackcb){

Util.assertMainThread();

longstartTime=LogTime.getLogTime();



EngineKeykey=keyFactory.buildKey(model,signature,width,height,transformations,

resourceClass,transcodeClass,options);



EngineResourcecached=loadFromCache(key,isMemoryCacheable);

if(cached!=null){

cb.onResourceReady(cached,DataSource.MEMORY_CACHE);

if(Log.isLogwww.wang027.comgable(TAG,Log.VERBOSE)){

logWithTimeAndKey("Loadedresourcefromcache",startTime,key);

}

returnnull;

}



EngineResourceactive=loadFromActiveResources(key,isMemoryCacheable);

if(active!=null){

cb.onResourceReady(active,DataSource.MEMORY_CACHE);

if(Log.isLoggable(TAG,Log.VERBOSE)){

logWithTimeAndKey("Loadedresourcefromactiveresources",startTime,key);

}

returnnull;

}



EngineJobcurrent=jobs.get(key);

if(current!=null){

current.addCallback(cb);

if(Log.isLoggable(TAG,Log.VERBOSE)){

logWithTimeAndKey("Addedtoexistingload",startTime,key);

}

returnnewLoadStatus(cb,current);

}



EngineJobengineJob=engineJobFactory.build(key,isMemoryCacheable,

useUnlimitedSourceExecutorPool);

DecodeJobdecodeJob=decodeJobFactory.build(

glideContext,

model,

key,

signature,

width,

height,

resourceClass,

transcodeClass,

priority,

diskCacheStrategy,

transformations,

isTransformationRequired,

options,

engineJob);

jobs.put(key,engineJob);

engineJob.addCallback(cb);

engineJob.start(decodeJob);



if(Log.isLoggable(TAG,Log.VERBOSE)){

logWithTimeAndKey("Startednewload",startTime,key);

}

returnnewLoadStatus(cb,engineJob);

}

在Engine.load中,先loadFromCache,如果缓存没有命中就再loadFromActiveResources,这是两级内存缓存,第一级是LruCache,第二级是ActiveCache,主要作用是,有可能一个图片很早就被加载了,可能已经从LruCache被移除掉了,但这个图片可能还在被某一个地方引用着,也就是还是Active的,那它就可能在将来仍被引用到,所以就把它保留在二级的ActiveCache中,ActiveCache中是以弱引用引用图片的,并通过ReferenceQueue监测弱引用的回收,然后用Handler.IdleHandler在CPU空闲时被被回收的引用项从ActiveCache中移除。



接下来看对应的Key是否已经正在加载,如果是的话,就addCallback,这样如果有多个地方同时请求同一张图片的话,只会生成一个加载任务,并都能收到回调,这点是比Universal-Image-Loader好的地方。



正常的加载流程是生成一个EngineJob和一个DecodeJob,通过engineJob.start(decodeJob)来进行实际的加载。



publicvoidstart(DecodeJobdecodeJob){

this.decodeJob=decodeJob;

GlideExecutorexecutor=decodeJob.willDecodeFromCache()

?diskCacheExecutor

:getActiveSourceExecutor();

executor.execute(decodeJob);

}

EngineJob.start直接将DecodeJob交给Executor去执行了(DecodeJob实现了Runnable接口)。DecodeJob的加载操作放到了runWrapped中



privatevoidrunWrapped(){

switch(runReason){

caseINITIALIZE:

stage=getNextStage(Stage.INITIALIZE);

currentGenerator=getNextGenerator();

runGenerators();

break;

caseSWITCH_TO_SOURCE_SERVICE:

runGenerators();

break;

caseDECODE_DATA:

decodeFromRetrievedData();

break;

default:

thrownewIllegalStateException("Unrecognizedrunreason:"+runReason);

}

}



privateDataFetcherGeneratorgetNextGenerator(){

switch(stage){

caseRESOURCE_CACHE:

returnnewResourceCacheGenerator(decodeHelper,this);

caseDATA_CACHE:

returnnewDataCacheGenerator(decodeHelper,this);

caseSOURCE:

returnnewSourceGenerator(decodeHelper,this);

caseFINISHED:

returnnull;

default:

thrownewIllegalStateException("Unrecognizedstage:"+stage);

}

}



privateStagegetNextStage(Stagecurrent){

switch(current){

caseINITIALIZE:

returndiskCacheStrategy.decodeCachedResource()

?Stage.RESOURCE_CACHE:getNextStage(Stage.RESOURCE_CACHE);

caseRESOURCE_CACHE:

returndiskCacheStrategy.decodeCachedData()

?Stage.DATA_CACHE:getNextStage(Stage.DATA_CACHE);

caseDATA_CACHE:

returnStage.SOURCE;

caseSOURCE:

caseFINISHED:

returnStage.FINISHED;

default:

thrownewIllegalArgumentException("Unrecognizedstage:"+current);

}

}

先获取当前的Stage

根据当前的Stage获取相应的Generator,

执行Generator

一共有三种Generator:

ResourceCacheGenerator:从处理过的缓存加载数据

DataCacheGenerator:从原始缓存加载数据

SourceGenerator:从数据源请求数据,如网络请求

前面说过,Glide的磁盘缓存可以选择缓存原始图片,缓存处理过的图片(如列表中显示缩略图时缩放后的图片),这三个Generator就分别对应处理过的图片缓存,原始图片缓存,和数据源加载。



在上面的第三步执行Generator时主要就是调用了Generator,其实就是执行Generator的startNext方法,这里以SourceGenerator为例。



publicbooleanstartNext(){

if(dataToCache!=null){

Objectdata=dataToCache;

dataToCache=null;

cacheData(data);

}



if(sourceCacheGenerator!=null&&sourceCacheGenerator.startNext()){

returntrue;

}

sourceCacheGenerator=null;



loadData=null;

booleanstarted=false;

while(!started&&hasNextModelLoader()){

loadData=helper.getLoadData().get(loadDataListIndex++);

if(loadData!=null

&&(helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())

||helper.hasLoadPath(loadData.fetcher.getDataClass()))){

started=true;

loadData.fetcher.loadData(helper.getPriority(),this);

}

}

returnstarted;

}

先忽略函数开始时dataToCache和sourceCacheGenerator相关的代码,第一次加载时这两个一定是null的。剩下的流程就是获取一个LoadData,调用LoadData.fetcher.loadData加载数据。看一下LoadData



List>getLoadData(){

if(!isLoadDataSet){

isLoadDataSet=true;

loadData.clear();

List>modelLoaders=glideContext.getRegistry().getModelLoaders(model);

intsize=modelLoaders.size();

for(inti=0;i
ModelLoadermodelLoader=modelLoaders.get(i);

LoadDatacurrent=

modelLoader.buildLoadData(model,width,height,options);

if(current!=null){

loadData.add(current);

}

}

}

returnloadData;

}

在getLoadData中通过获取所有提前注册过的能处理Model类型的ModelLoader,调用它的buildLoadData生成LoadData,最终返回一个LoadData列表。



前面说过LoadData包含了用来获取数据的DataFetcher。SourceGenerator.startNext就调用了loadData.fetcher.loadData来进行加载数据,并传进去一个Callback,就是当前的SourceGenerator,如果加载成功,会调用onDataReady



publicvoidonDataReady(Objectdata){

DiskCacheStrategydiskCacheStrategy=helper.getDiskCacheStrategy();

if(data!=null&&diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())){

dataToCache=data;

//Wemightbebeingcalledbackonsomeoneelse''sthread.Beforedoinganything,weshould

//rescheduletogetbackontoGlide''sthread.

cb.reschedule();

}else{

cb.onDataFetcherReady(loadData.sourceKey,data,loadData.fetcher,

loadData.fetcher.getDataSource(),originalKey);

}

}

数据加载成功后,如果设置了要进行磁盘缓存,会设置成员变量dataToCache,并调用Callback的reschedule,结果就是会再次调用当前Generator的startNext,startNext的前半部分实现就起作用了,会进行写缓存的操作。



当rescheudle后写了缓存后,或不缓存的情况下,会调用onDataFetcherReady,这个Callback就是前面的DecodeJob,在onDataFetcherReady中会调用decodeFromRetrievedDatadecode数据,最终调用到decodeFromFetcher



privateResourcedecodeFromFetcher(Datadata,DataSourcedataSource)

throwsGlideException{

LoadPathpath=decodeHelper.getLoadPath((Class)data.getClass());

returnrunLoadPath(data,dataSource,path);

}

获取LoadPath,并调用它的load方法。LoadPath就是封装了多个DecodePath,DecodePath用于decodeandTransform数据,如InputStream->Bitmap->BitmapDrawable,DecodePath中会获取预先注册的Decoder来decode获取到的数据,decode成功后通过回调调用DecodeJob的onResourceDecoded方法。



publicResourceonResourceDecoded(Resourcedecoded){

ClassresourceSubClass=getResourceClass(decoded);

TransformationappliedTransformation=null;

Resourcetransformed=decoded;

if(dataSource!=DataSource.RESOURCE_DISK_CACHE){

appliedTransformation=decodeHelper.getTransformation(resourceSubClass);

transformed=appliedTransformation.transform(decoded,width,height);//////////////////////////1

}

//TODO:MakethistheresponsibilityoftheTransformation.

if(!decoded.equals(transformed)){

decoded.recycle();

}



finalEncodeStrategyencodeStrategy;

finalResourceEncoderencoder;

if(decodeHelper.isResourceEncoderAvailable(transformed)){

encoder=decodeHelper.getResultEncoder(transformed);

encodeStrategy=encoder.getEncodeStrategy(options);

}else{

encoder=null;

encodeStrategy=EncodeStrategy.NONE;

}



Resourceresult=transformed;

booleanisFromAlternateCacheKey=!decodeHelper.isSourceKey(currentSourceKey);

if(diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey,dataSource,

encodeStrategy)){

if(encoder==null){

thrownewRegistry.NoResultEncoderAvailableException(transformed.get().getClass());

}

finalKeykey;

if(encodeStrategy==EncodeStrategy.SOURCE){

key=newDataCacheKey(currentSourceKey,signature);

}elseif(encodeStrategy==EncodeStrategy.TRANSFORMED){

key=newResourceCacheKey(currentSourceKey,signature,width,height,

appliedTransformation,resourceSubClass,options);

}else{

thrownewIllegalArgumentException("Unknownstrategy:"+encodeStrategy);

}



LockedResourcelockedResult=LockedResource.obtain(transformed);

deferredEncodeManager.init(key,encoder,lockedResult);//////////////////////////2

result=lockedResult;

}

returnresult;

}

在上述代码的注释1处对加载成功的资源应用Transformation,然后在注释2处根据缓存策略初始化DeferredEncodeManager,在前面的decodeFromRetrievedData中,如果有必要会把transform过的资源写缓存。



privatevoiddecodeFromRetrievedData(){

...



if(resource!=null){

notifyEncodeAndRelease(resource,currentDataSource);

}else{

runGenerators();

}

}

notifyEncodeAndRelease中处理了对处理过的图片的缓存操作。当缓存完成后(如果有需要的话)就通过回调告诉外面加载完成了。至此,整个加载过程完成。



Glide配置



Glide允许我们进行一定程度的自定义,比如设置自定义的Executor,设置缓存池,设置Log等级等,完成这个任务的类叫GlideBuilder,Glide类在工程中是作为单例使用的,看一下代码:



publicstaticGlideget(Contextcontext){

if(glide==null){

synchronized(Glide.class){

if(glide==null){

ContextapplicationContext=context.getApplicationContext();

Listmodules=newManifestParser(applicationContext).parse();



GlideBuilderbuilder=newGlideBuilder(applicationContext);

for(GlideModulemodule:modules){

module.applyOptions(applicationContext,builder);

}

glide=builder.createGlide();

for(GlideModulemodule:modules){

module.registerComponents(applicationContext,glide.registry);

}

}

}

}



returnglide;

}

通过GlideBuilder生成了一个Glide实例,我们是没有办法直接配置GlideBuilder的,但我们发现Glide.get解析了Manifest,获取了一个GlideModule的列表,并调用了它的applyOptions和registerComponents方法。以项目中OkHttp的配置为例



publicclassOkHttpGlideModuleimplementsGlideModule{

@Override

publicvoidapplyOptions(Contextcontext,GlideBuilderbuilder){

//Donothing.

}



@Override

publicvoidregisterComponents(Contextcontext,Registryregistry){

registry.replace(GlideUrl.class,InputStream.class,newOkHttpUrlLoader.Factory());

}

}

GlideModule有两个方法,applyOptions,有一个GlideBuilder参数,在这里我们就可以配置Glide了。还有一个registerComponents方法,并有一个Registry参数,通过这个类的实例我们就可以注册我们自定义的ModelLoader,Encoder等基础组件了。



自定义GlideModule是通过Manifest的meta-data标签配置的




android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"

android:value="GlideModule"/>

献花(0)
+1
(本文系thedust79首藏)