2020一线互联网三方源码高频面试总结_第1页
2020一线互联网三方源码高频面试总结_第2页
2020一线互联网三方源码高频面试总结_第3页
2020一线互联网三方源码高频面试总结_第4页
2020一线互联网三方源码高频面试总结_第5页
已阅读5页,还剩161页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

2020一线互联网源码高频面试总Glide:加载、缓存、LRU算法(如何自己设计一个大图加载框架)(LRUCache原如何阅读源在开始解析Glide源码之前,先和大家谈一下该如何阅读源码,这个问题也是我平时被问得比较多的,因为很多人都觉得阅读源码是一件比较的事情。GlideGlide的代码写得不好,只是因为Glide和复杂程度和Volley完全不是在一个量级上的。家白就会觉己是盲摸象也研不。果是去析主体的实现逻辑,那么就有比较明确的目的性,这样阅读源码会更加轻松,也更加有成效。而今天带大家阅读的Glide源码就非常适合使用这个技巧,因为Glide的源码太复杂了,千万不要试图去搞明白它每行代码的作用,而是应该只分析它的主体实现逻辑。那么本篇文到底是如何实现将一张网络展示到ImageView上面的。先将Glide的一整套加载机制的基本流程梳理清楚,然后再通过后面的几篇文章具体去了解Glide源码方方面面的细源码既然是要阅读Glide的源码,那么自然需要先将Glide的源码下来。其实如果你使用在build.gradle中添加依赖的方式将Glide引入到项目中的,那么源码自动就已经下来了,在AndroidStudio中就可以直接进行查看。不过,使用添加依赖的方式引入的Glide,只能看到它的源码,但不能做任何的修改,如果你还需要修改它的源码的话,可以到上面将它的完整源码下来。Glide 不过在这个地址到的都是的源码,有可能还正在处于开发当中。而整个系列都是使用Glide3.7.0这个版本来进行讲解的,因此如果你需要专门去3.7.0版本的源码,可以到这个地址进行: 开始阅最后into()。那么开始一步步阅读这三步走的源码,先从with()看起。with()方法的方法重载:publicclassGlidepublicstaticRequestManagerwith(Contextreturnretriever.get(context);}publicstaticRequestManagerwith(Activityreturnretriever.get(activity);}publicstaticRequestManagerwith(FragmentActivityreturnretriever.get(activity);}@ returnretriever.get(fragment);}returnretriever.get(fragment);}}可以看到,with()方法的重载种类非常多,既可以传入Activity,也可以传入Fragment或者是Context。每一个with()方法重载的代码都非常简单,都是先调用RequestManagerRetriever的静态get()方法得到一个RequestManagerRetriever对象,这个静态get()方法就是一个单例实现,没什么需要解释的。然后再调用RequestManagerRetriever的实例get()方法,去获取RequestManager对象。而RequestManagerRetriever的实例get()方法中的逻辑是什么样的呢?一起来看一看publicclassRequestManagerRetrieverimplementsHandler.CallbackprivatestaticfinalRequestManagerRetrieverINSTANCE=newRequestManagerRetriever();privatevolatileRequestManagerapplicationManager;{return}privateRequestManagergetApplicationManager(Contextcontext)//Eitheranapplicationcontextorwe'reonabackgroundthread.if(applicationManager==null){//Normallypause/resumeistakencareofbythefragmentweaddthefragmentoractivity.willnotreceivelifecycle

//However,inthiscasesincethemanagerattachedtothe//events,wemustforcethemanagertostartresumedusing }}}}{if(context==null){if(contextinstanceofFragmentActivity){returnget((FragmentActivity)context);{returnget((Activity)returnget(((ContextWrapper)}}}{if(Util.isOnBackgroundThread())}elsereturnsupportFragmentGet(activity,fm);}}{if(fragment.getActivity()==null)thrownewIllegalArgumentException("Youcannotstartaloadonafragmentbeforeitisattached");}if(Util.isOnBackgroundThread())}elseFragmentManagerfm=fragment.getChildFragmentManager();returnsupportFragmentGet(fragment.getActivity(),fm);}} publicRequestManagerget(Activityactivity){ B){}elsereturnfragmentGet(activity,fm);}}@ activity.isDestroyed())thrownewIllegalArgumentException("Youcannotstartaloadfora}}@{if(fragment.getActivity()==null)thrownewIllegalArgumentException("Youcannotstartaloadonafragmentbeforeitisattached");} Build.VERSION_CODES.JELLY_BEAN_MR1){}elseandroid.app.FragmentManagerfm=fragment.getChildFragmentManager();returnfragmentGet(fragment.getActivity(),fm);}}@ android.app.FragmentManagerfm){ if(current==null)if(current==null){current=newRequestManagerFragment();}}}@ {RequestManagerFragmentcurrent=getRequestManagerFragment(fm);RequestManagerrequestManager=current.getRequestManager();if(requestManager==null) }} FragmentManagerfm){ if(current==null)if(current==null){current=newSupportRequestManagerFragment();}}}{SupportRequestManagerFragment RequestManagerrequestManager=current.getRequestManager();if(requestManager==null){ }}

复杂,RequestManagerRetrieverget()Context参数,Activity参数,FragmentApplication类型的参数,和传入非Application类型的参数。对象,那么这里就会调用带有Context参数的get方法重载,然后会在第44行调用ApplicationGlide并不需要做什么特殊的处理,它自动就是和应用程序的生命周期是同步的,如果应用程序关闭的话,Glide的加载ActivityFragment。具体添加的逻辑是在上述代码的如果你在某个Activity上正在加载着一片,结果还没加载出来,Activity就被用户关掉了,那么还应该继续加载吗?当然不应该。Glide并没有办法知道Activity的生GlideFragmentFragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以到的,这样Glide就可以这里额外再提一句,从第48行代码可以看出,如果是在非主线程当中使用的Glide,那么不管你是传入的Activity还是Fragment,都会被强制当成Application来处理。不过其实这就属于是在分析代码的细节了,本篇文章会把目光主要放在Glide的主线工作流程上面,总体来说,第一个with()方法的源码还是比较好理解的。其实就是为了得到一个RequestManager对象而已,然后Glide会根据传入with()方法的参数来确定加载的由于with()方法返回的是一个RequestManager对象,那么很容易就能想到,load()RequestManager类当中的,所以说首先要看的就是RequestManager这个类。不过在上一篇文章中学过,Glide是支持URL字符串、本地路径等等加载形式的,因此RequestManager中也有很多个load()方法的重载。但是这里不可能把每个load()方法的重载都看一遍,因此就只选其中一个加载URL字符串的load()方法来进行研究吧。RequestManager类的简化代码如下所示Returnsarequestbuildertoloadthegiven{@link@see@see return(DrawableTypeRequest<String>)}Returnsarequestbuilderthatloadsdatafrom{@linkString}susinganemptyNote-thismethodcachesdatausingonlythegivenStringasthecachekey.IfthedataisaUrioutsideofyourcontrol,oryouotherwiseexpectthedatarepresentedbythegivenStringtochangewithouttheStringyoucreatethatidentifiesthedurrentlyatthegivenStringthatwillinvalidatethecacheifthatdata{@linkDrawableRequestBuilder#skipMemoryCache(boolean)}maybe*@see@see{return} Glide.buildStreamModelLoader(modelClass,context);ModelLoader<T,ParcelFileDescriptor>fileDescriptorModelLoader=if(modelClass!=null&&streamModelLoader==null&&fileDescriptorModelLoadernull)thrownewIllegalArgumentException("Unknowntype"+modelClass+".YouprovideaModelofatype+"whichthereisaregisteredModelLoader,ifyouareusingacustommodel,youmustcall"+"Glide#registerwithaModelLoaderFactoryforyourcustom

} fileDescriptorModelLoader,}}RequestManager类的代码是非常多的,但是经过我这样简化之后,看上去就比较清爽了。在只探究加载URL字符串这一个load()方法的情况下,那么比较重要的方法就只剩那么先来看load()方法,这个方法中的逻辑是非常简单的,只有一行代码,就是先调用了fromString()方法,再调用load()方法,然后把传入的URL地址传进去。而fromString()方法也极为简单,就是调用了loadGeneric()方法,并且指定参数为String.class,因为load()方法传入的是一个字符串参数。那么看上去,好像主要的工作都是在loadGeneric()方法中进行的其实lodGeneic()方法也没几行代码,这里分别调用了Glie.bildSteamMoelLoader()方法和Glie.ildileDecitorModelLoder()方法来获得MoelLoaderModelLader对象是用于加载的,而给load()方法传入不同类型的参数,这里也会得到不同的ModelLoder对象。不过bildteamModelLader()方法的逻辑还是蛮复杂的,这里就不展开介绍了,要不然篇幅实在收不住,感的话你可以自己研究。由于刚才传入的参数是ti.class,因此最终得到的是teamStiLoader对象,它是实现了MoelLoader接口的。最后可以看到,loadGeneric()方法是要返回一个DrawableTypeRequest对象的,因此在loadGeneric()方法的最后又去new了一个DrawableTypeRequest对象,然后把刚才获得的ModelLoader对象,还有一大堆杂七杂八的东西都传了进去。具体每个参数的含义和作用就那么这个DrawableTypeRequest的作用是什么呢?来看下它的源码,如下所示publicclassDrawableTypeRequest<ModelType>extendsDrawableRequestBuilder<ModelType>implementsDownloadOptions{privatefinalModelLoader<ModelType,InputStream>privatefinalModelLoader<ModelType,ParcelFileDescriptor>fileDescriptorModelLoader;privatefinalRequestManager.OptionsApplieroptionsApplier;privatestatic<A,Z,R>FixedLoadProvider<A,ImageWrapper,Z,R>buildProvider(Glide

ModelLoader<A,InputStream> Class<R>transcodedClass,if(streamModelLoader==null&&fileDescriptorModelLoader=={return}if(transcoder==null)transcoder=glide.buildTranscoder(resourceClass,} returnnewFixedLoadProvider<A,ImageWrapper,Z,R>(modelLoader,transcoder,} InputStream>streamModelLoader, Contextcontext,Glideglide,optionsApplier){super(context, glide,requestTracker,lifecycle);this.streamModelLoader=streamModelLoader;this.optionsApplier=optionsApplier;}Attemptstoalwaysloadtheresourceasa{@linkandroid.graphics.Bitmap},evenifitcouldactuallybeanimated.*@returnAnewrequestbuilderforloadinga{@linkpublicBitmapTypeRequest<ModelType>asBitmap() fileDescriptorModelLoader,} *IftheunderlyingdataisnotaGIF,thiswillfail.Asaresult,thisshouldonlybeusedifthemodelrepresentsananimatedGIFandthecallerwantstointeractwiththeGIfDrawabledirectly.Normallyusingjustan{@linkDrawableTypeRequest}issufficientbecauseitwilldeterminewhetherornotthegivendatarepresentsananimatedGIFandreturntheappropriateanimatedornotanimated{@linkandroid.graphics.drawable.Drawable}* publicGifTypeRequest<ModelType>asGif() streamModelLoader,optionsApplier));}}这个类中的代码本身就不多,稍微做了一点简化。可以看到,最主要的就是它提供了sBitmap()和aGif()这两个方法。这两个方法在上一篇文章当中都是学过的,分别是用于强制指定加载静态和动态。而从源码中可以看出,它们分别又创建了一个BitmapypeReqest和Gifyeeqet,如果没有进行强制指定的话,那默认就是使用Dawaleyeeqet。好的,那么再回到RequestManager的load()方法中。刚才已经分析过了,fromString()方返回一个DrawableTypeRequest对象,接下来会调用这个对象的load()方法,把的URL地址传进去。但是刚才看到了,DrawableTypeRequest中并没有load()方法,那么publicclassextendsGenericRequestBuilder<ModelType,ImageWrapper,GifBitmapWrapper,implementsBitmapOptions,DrawableOptionsDrawableRequestBuilder(Contextcontext,Class<ModelType>modelClass, GlideDrawable>loadProvider,GlideRequestTrackerrequestTracker,Lifecyclelifecycle)super(context,modelClass,loadProvider,GlideDrawable.class,glide,}

//Defaulttoanimating.publicDrawableRequestBuilder<ModelType>thumbnail(DrawableRequestBuilder<?>thumbnailRequest){returnthis;}publicthumbnail(GenericRequestBuilder<?,?,?,GlideDrawable>thumbnailRequest){returnthis;}returnthis;}returnthis;} returnthis;} GifBitmapWrapper>cacheDecoder){returnthis;} returnthis;}returnthis;} transformations){}{return}{return}publicDrawableRequestBuilder<ModelType>bitmapTransform(Transformation<Bitmap>...bitmapTransformations){GifBitmapWrapperTransformation[]transformationsfor(inti=0;i<bitmapTransformations.length;i++){transformations[i]=newGifBitmapWrapperTransformation(glide.getBitmapPool(),}} returnthis;}publicreturn}returnthis;}returnthis;}publicDrawableRequestBuilder<ModelType>crossFade(intanimationId,intduration)}returnthis;} animator){returnthis;}returnthis;}returnthis;}returnthis;}returnthis;}publicDrawableRequestBuilder<ModelType>fallback(intresourceId)returnthis;}returnthis;}returnthis;}publicDrawableRequestBuilder<ModelType>returnthis;}publicDrawableRequestBuilder<ModelType>diskCacheStrategy(DiskCacheStrategy{returnthis;}returnthis;}{super.override(width,height);returnthis;}sourceEncoder){returnthis;}returnthis;}returnthis;}returnthis;}publicDrawableRequestBuilder<ModelType>clone()return(DrawableRequestBuilder<ModelType>)}{return}{}{}}有不少在上篇文章中已经用过了,比如说placeholder()方法、error()方法、diskCacheStrategy()方法、override()方法等。当然还有很多暂时还没用到的API,会在后load()DrawableRequestBuilderinto()方法(220行)load()方法返回的其实就是一个DrawableTypeRequest对象。那么接下来就要进行第三步了,into()方法中的逻辑。如果说前面两步都是在准备开胃小菜的话,那么现在终于要进入主菜了,因为ino()方法也是整个Glie加载流程中逻辑最复杂的地方。不过从刚才的代码来看,into()o(view)。那么很显然,into()方法的具体逻辑都是在DrawableRequestBuilder的父类当中了。 的父类是GenericRequestBuilder,来看一GenericRequestBuilder类中的into()方法,如下所示if(view==null){}{switch(view.getScaleType()){caseCENTER_CROP:caseFIT_START:caseFIT_END://$CASES-//Do}}returninto(glide.buildImageView(view,}这里前面一大堆的判断逻辑都可以先不用管,等到后面文章讲transform的时候会再进行解释,现在只需要关注最后一行代码。最后一行代码先是调用了glide.buildImageView()方法,这个方构建出一个对象,对象则是用来最终展示用的,如果跟进去的话会看到如下代码:{returnimageViewFactory.build(imageView,}publicclassImageView{}else+",try}}}你要分析这个class参数是从哪儿传过来的,这可有得你分析了,简单起见我直接帮大家梳了asBitmap()方法,那么这里就会构建出BitmapImageView对象,否则的话构建的都是GlideDrawableImageView 对象。至于上述代码中的DrawableImageView对象,这个通常都是用不到的,可以暂时不用管它。也就是说,通过glide.bilImageView()方法,构建出了一个GlieDawaleImageView对象。那现在回到刚才ino()方法的最后一行,可以看到,这里又将这个参数传入到了GeneicRequestBiler另一个接收对象的ito()方法当中了。我们来看一下这个ito()方法的源码:if(==null){}}Requestprevious=if(previous!={previous.clear();}Requestrequest=buildRequest();return;}建出了一个Request对象,还有第18行来执行这个Request。Request是用来发出加载请求的,它是Glide中非常关键的一个组件。先来buildRequest()方法是如何构建Request{if(priority==null)priority=}returnbuildRequestRecursive(,} ThumbnailReqoordinatorparentCoordinator){{if(isThumbnailBuilt)thrownewIllegalStateException("Youcannotusearequestasboththemainrequestandathumbnail,"+"considerusingclone()ontherequest(s)passedto}if}if(thumbnailRequestBuilder.priority==}if(Util.isValidDimensions(overrideWidth,thumbnailRequestBuilder.overrideHeight)){thumbnailRequestBuilder.override(overrideWidth,} isThumbnailBuilt=true; isThumbnailBuilt=false;returncoordinator;}elseif(thumbSizeMultiplier!=null)//Basecase:thumbnailmultipliergeneratesathumbnailrequest,butcannotrecurse. RequestfullRequest=obtainRequest(,sizeMultiplier,priority,coordinator); getThumbnailPriority(),coordinator);returncoordinator;}else//Basecase:noreturnobtainRequest(,sizeMultiplier,priority,}}privateRequestobtainRequest(<TranscodeType>,floatsizeMultiplier,Priority {returnGenericRequest.obtain(,}可以看到,buildRequest()方法的其实又调用了buildRequestRecursive()方法,而buildRequestRecursive()方法中的代码虽然有点长,但是其中90的代码都是在处理缩略图的。如果只追主线流程的话,那么只需要看第47行代码就可以了。这里调用了obtainRequest方法来获取一Request对象obtainRequest()方法中又去调用了GenericRequest的obtain()方法。注意这个obtain()方法需要传入非常多的参数,而其中很多的参数都是比较熟悉的,像什么placeholderId、errorPlaceholder、diskCacheStrategy等等。因此,就有理由猜测,刚才在load()方法中调用的所有API,其实都是在这里组装到Request对象当中的。那么进入到这个GenericRequest的obtain()方法瞧一瞧:publicfinalclassGenericRequest<A,T,Z,R>implementsRequest,SizeReadyCallback,ResourceCallback{publicstatic<A,T,Z,R>GenericRequest<A,T,Z,R>obtain(LoadProvider<A,T,Z,R>loadProvider,Amodel,Keysignature,Contextcontext,Prioritypriority,<R>intplaceholderResourceId,DrawableerrorDrawable,interrorResourceId,intfallbackResourceId,RequestListener<?superA,R>requestListener,Reqoordinatorreqoordinator,Engineengine,Class<R>transcodeClass,booleanisMemoryCacheable,intoverrideWidth,int{ if(request==null)}}

,可以看到,这里在33newGenericRequest对象,并在最后一行返回,也就是说,obtain()方法实际上获得的就是一个GenericRequest对象。另外这里又在第35行调用了GenericRequest的init()里面主要就是一些赋值的代码,将传入的这些参数赋值到GenericRequest的成员变量当中,就不再跟进去看了好,那现在解决了构建Request对象的问题,接下来看一下这个Request对象又是怎么执行的。回到刚才的into()方法,你会发现在第18行调用了requestTracker.runRequest()方法来去执行这个Request,那么跟进去瞧一瞧,如下所示:*Startstrackingthegivenif(!isPaused){}else}}这里有一个简单的逻辑判断,就是先判断Glide当前是不是处理暂停状态,如果不是暂停状态就调用Request的begin()方法来执行Request,否则的话就先将Request添加到待执行队暂停请求的功能仍然不是这篇文章所关心的,这里就直接忽略了,重点来看这个begin()方法。由于当前的Request对象是一个GenericRequest,因此这里就需要看GenericRequest中的begin()方法了,如下所示:publicvoidbegin()if(model==null){}status={onSizeReady(overrideWidth,}else} {}logV("finishedrunmethodin"+ }}这里来注意几个细节,首先如果model等于null,model也就是在第二步load()方法中传入的URL地址,这个时候会调用onException()方法。如果你跟到onException()setErrorPlaceholder()当中,如下所示:{if(!canNotifyStatusChanged()){}Drawableerror=model==null?getFallbackDrawable():null;if(error==null){error=}if(error==null)error=}.onLoadFailed(e,}errorloading占位图,然后调用.onLoadFailed()方法并将占位图传入。那么onLoadFailed()方法中做了什么呢? classImageView<Z>extendsView<ImageView,Z>implementsGlideAnimation.ViewAdapter{{}{}}很简单,其实就是将这张error占位图显示到ImageView上而已,因为现在出现了异常,没办法展示正常的了。而如果你仔细看下刚才begin()方法的第15行,你会发现它又调用了一个.onLoadStarted()方法,并传入了一个loading占位图,在也就说,在请求开始之前,会先使用这张占位图代替最终的显示。这也是在上一篇文章中学过的placeholder()和error()这两个占位图API底层的实现原理。好,那么继续回到begin()方法。刚才讲了占位图的实现,那么具体的加载又是从哪里开始的呢?是在begin()方法的第10行和第12行。这里要分两种情况,一种是你使用了verride()PI为指定了一个固定的宽高,一种是没有指定。如果指定了的话,就会执行第10行代码,调用onieReady()方法。如果没指定的话,就会执行第12行代码,调用.getie()方法。这个.getize()方法的会根据ImageView的lau_ithlo_heigt值做一系列的计算,来算出应该的宽高。具体的计算细节我就不带着大家分析了,总之在计算完之后,它也会调用nieReay()方法。也就是说,不管是哪种情况,最终都会调用到oieReay():{if(Log.isLoggable(TAG,Log.VERBOSE))logV("GotonSizeReadyin"+ }{}status=width=Math.round(sizeMultiplier*width);height=Math.round(sizeMultiplier*height);finalDataFetcher<T>dataFetcher=modelLoader.getResourceFetcher(model,width,height);if(dataFetcher==null){onException(newException("Failedtoloadmodel:\'"+model+"\'"));}if(Log.isLoggable(TAG,Log.VERBOSE)){logV("finishedsetupforcallingloadin"+ }loadStatus=engine.load(signature,width,height,dataFetcher,loadProvider,transformation,loadedFromMemoryCache=resource!=null;logV("finishedonSizeReadyin"+ }}从这里开始,真正复杂的地方来了,需要慢慢进行分析。先来看一下,在第12行调用了loaPoide.getMoelLoder()方法,那么第一个要搞清楚的就是,这个lodPrier是什么?要搞清楚这点,需要先回到第二步的lod()方法当中。还记得lad()方法是返回一个Dawleyeeuest对象吗?刚才只是分析了Dawaleyeeqet当中的asBitmap()和asGif()方法,并没有仔细看它的构造函数,现在重新来看一DrawableTypeRequest类的构造函数publicclassDrawableTypeRequest<ModelType>extendsDrawableRequestBuilder<ModelType>implementsDownloadOptions{privatefinalModelLoader<ModelType,InputStream>privatefinalModelLoader<ModelType,ParcelFileDescriptor>fileDescriptorModelLoader;privatefinalRequestManager.OptionsApplieroptionsApplier;privatestatic<A,Z,R>FixedLoadProvider<A,ImageWrapper,Z,R>buildProvider(Glide

ModelLoader<A,InputStream> Class<R>transcodedClass,if(streamModelLoader==null&&fileDescriptorModelLoader=={return}if(transcoder==null)transcoder=glide.buildTranscoder(resourceClass,} returnnewFixedLoadProvider<A,ImageWrapper,Z,R>(modelLoader,transcoder,} InputStream>streamModelLoader, Contextcontext,Glideglide,optionsApplier){super(context, glide,requestTracker,lifecycle);}}

this.streamModelLoader=streamModelLoader;this.optionsApplier=optionsApplier;可以看到,这里在第29行,也就是构造函数中,调用了一个buildProvider()方法,并把streamModelLoaderfileDescriptorModelLoader等参数传入到这个方法中,这两个ModelLoader就是之前在loadGeneric()方法中构建出来的。那么再来看一下bildPrier()方法里面做了什么,在第6行调用glide.bildancoder()方法来构建一个esucencoer,它是用于对进行转码的,由于eouceancder是一个接口,这里实际会构建出一个GiBitmapWaperDaaleacoer对象。接下来在第18行调用了glide.buildDataProvider()方法来构建一个DataLoadProvider,它是用于对进行编的,由于DataLoadProvider是一个接口,这里实际会构建出一个ImageGifDrawableLoadProvider对象。然后在20行,new了一ImageModelLoader的实例,并把loadGeneric()方法中构建的两个ModelLoader封装到了ImageModelLoader当中。最后,在第22行,new出一个FixedLoadProvider,并把刚才构建的出来的 、ImageModelLoader ImageGifDrawableLoadProvider都封装进去,这个也就是onSizeReady()方法中的loadProvider了。好的,那么回到onSizeReady()方法中,在onSizeReady()方法的第12行和第18行,分别调用了loadProvider的getModelLoader()方法和getTranscoder()么得到的对象也就是刚才分析的ImageModelLoader和GifBitmapWrapperDrawableTranscoder了。而在13行,又调用了ImageModelLoader的getResourceFetcher()方法,这里又需要跟进publicclassImageModelLoader<A>implementsModelLoader<A,Image{privatestaticfinalStringTAG=ModelLoader<A,ParcelFileDescriptor>fileDescriptorLoader){if(streamLoader==null&&fileDescriptorLoader==null) }this.streamLoader=streamLoader;}{if(streamLoader!=null){streamFetcher=streamLoader.getResourceFetcher(model,width,

if(fileDescriptorLoader!=null){fileDescriptorFetcher=fileDescriptorLoader.getResourceFetcher(model,}returnnewImageFetcher(streamFetcher,}else}}{privatefinalDataFetcher<InputStream>privatefinalDataFetcher<ParcelFileDescriptor>this.streamFetcher=streamFetcher;}}}而这个streamLoader其实就是在loadGeneric()方法中构建出的StreamStringLoader,调用它的getResourceFetcher()方得到一个HttpUrlFetcher对象。然后在第28行new出了一个ImageFetcher对象,并把获得的HttpUrlFetcher对象传进去。也就是说,ImageModelLoadergetResourceFetcher()方法得到的是一个ImageFetcher。那么再次回到onSizeReady()方法,在onSizeReady()方法的第23行,这里将刚才获得的ImageFetcher、GifBitmapWrapperDrawableTranscoder等等一系列的值一起传入到了Engine的load()方法当中。接下来就要看一看,这个Engine的load()方法当中,到底做publicclassEngineimplemengineJobListener,EngineResource.ResourceListener{public<T,Z,R>LoadStatusload(Keysignature,intwidth,intheight,DataFetcher<T>fetcher, diskCacheStrategy,ResourceCallbackcb){longstartTime=finalStringid= transcoder,loadProvider.getSourceEncoder());EngineResource<?>cached=loadFromCache(key,isMemoryCacheable);if(cached!=null){}}

if(active!=null){logWithTimeAndKey("Loadedresourcefromactiveresources",}}if(current!=null){if(Log.isLoggable(TAG,{logWithTimeAndKey("Addedtoexistingload",startTime,}}DecodeJob<T,Z,R>decodeJob=newDecodeJob<T,Z,R>(key,width,height,fetcher,loadProvider,transformation,transcoder,diskCacheProvider,diskCacheStrategy,priority);EngineRunnablerunnable=newEngineRunnable(engineJob,decodeJob,priority);jobs.put(key,engineJob);if(Log.isLoggable(TAG,}}

lod()方法中的代码虽然有点长,但大多数的代码都是在处理缓存的。关于缓存的内容会在下一篇文章当中学习,现在只需要从第45行看起就行。这里构建了一个EineJb主要作用就是用来开启线程的,为后面的异步加载做准备。接下来第46行创建了一个DecodeJob对象,从名字上来看,它好像是用来对进行的,但实际上它的任务十分繁重,待会就知道了。继续往下看,第48行创建了一个EineRunnle对象,并且在51行调用了EineJob的sat()方法来运行EngineRunale对象,这实际上就是让EineRunnle的run)方法在子线程当中执行了。那么现在就可以去看看EineRunnle的rn()方法里做了些什么,如下所示:publicvoidrun(){{}Exceptionexception=null;try{resource=}catch(Exceptione)if(Log.isLoggable(TAG,}exception=}if(isCancelled)if(resource!={}}if(resource=={}else}}这个方法中的代码并不多,但仍然还是要抓重点。在第9行,这里调用了一个decode()方法,并且这个方法返回了一个Resource对象。看上去所有的逻辑应该都在这个decode()方法执行的了,那跟进去瞧一瞧:{if(isDecodingFromCache()){}else}}ecode()方法中又分了两种情况,从缓存当中去ecde的话就会执ecodeFroCce(),否则的话就执行ecodeFromSouce)。本篇文章中不缓存的情况,那么就直接来看decdeFromSouce()方法的代码吧,如下所示:{return}{Resource<T>decoded=decodeSource();}{Resource<T>decoded=null;try{longstartTime=LogTime.getLogTime();finalAdata=fetcher.loadData(priority);if(Log.isLoggable(TAG,Log.VERBOSE)){logWithTimeAndKey("Fetcheddata",}if(isCancelled)}decoded=}finally}}

主要的方法就这些,我都帮大家提取出来了。那么先来看一下decodeFromSource()方法,其实它的工作分为两部,第一步是调用decodeSource()方法来获得一个Resource对象,第二步是调用transformEncodeAndTranscode()Resource对象。那么先来看第一步,decodeSource()方法中的逻辑也并不复杂,首先在第14行调用了fetcher.loadData()方法。那么这个fetcher是什么呢?其实就是刚才在onSizeReady()方法中得到的ImageFetcher对象,这里调用它的loadData()方法,代码如下所示:{InputStreamis=null;{tryis=}catch(Exceptione)}}}}ParcelFileDescriptorfileDescriptor=null;if(fileDescriptorFetcher!=null){try}catch(Exceptione)Log.v(TAG,"ExceptionfetchingParcelFileDescriptor",}if(is==null)}}}}ImageFetcherloadData()6streamFetcher.loadData()方法,那么这个streamFetcher是什么呢?自然就是刚才在组装ImageFetcher对象时传进来HttpUrlFetcher了。因此这里又HttpUrlFetcher的returnloadDataWithRedirects(glideUrl.toURL(),0/*redirects*/,null/*lastUrl*/,}privateInputStreamloadDataWithRedirects(URLurl,intredirects,URLlastUrl,Map<String,String>headers)if(redirects>=UM_REDIRECTS)thrownewIOException("Toomany(>"+UM_REDIRECTS+")}else//ComparingtheURLsusing.equalsperformsadditionalnetworkI/Oandisgenerallybroken. /2006/11/javaneturlequals-and-hashcode-make.html.try

{thrownewIOException("Inre-direct}//Donothing,thisisbest}}urlConnection=for(Map.Entry<String,String>headerEntry:}//Connectexplicitlytoavoiderrorsindecodersifconnectionfails.if(isCancelled)}finalintstatusCode=urlConnection.getResponseCode();if(statusCode/100==2){}elseif(statusCode/100==3)if(TextUtils.isEmpty(redirectUrlString)){}URLredirectUrl=newURL(url,}elseif(statusCode==-1) } }}throwsIOException{if{intcontentLength= }else }stream=}}

经过一层一层地跋山涉水,终于在这里找到网络通讯的代码了!之前有朋友跟我讲过,说Glie的源码实在是太复杂了,甚至连网络请求是在哪里发出去的都找不到。也是经过一段一段又一段的代码,终于把网络请求的代码给找出来了,实在是太不容易了。不过也别高兴得太早,现在离最终分析完还早着呢。可以看到,loadDaa()方法只是返回了一个Inputteam,服务器返回的数据连读都还没开始读呢。所以还是要静下心来继续分析,回到刚才Iageecer的loadDaa()方法中,在这个方法的最后一行,创建了一个Imageaper对象,并把刚才得到的Ipttream作为参数传了进去。然后回到再上一层,也就是DecodeJob的decodeSource()方法当中,在得到了这个ImageWrapperdecodeFromSourceData()当中,来去这个对象。decodeFromSourceData()方法的代码如下所示:{finalResource<T>if(diskCacheStrategy.cacheSource())decoded=}elselongstartTime=if(Log.isLoggable(TAG,Log.VERBOSE)){logWithTimeAndKey("Decodedfromsource",}}} 就是刚才在onSizeReady()方法中得到的FixedLoadProvider,而得到的则是一个 对象也就是要调用这个对象的方法来对进行。那么来看下 的代码 ResourceDecoder<ImageWrapper,GifBitmapWrapper>{publicResource<GifBitmapWrapper>decode(ImageWrappersource,intwidth,intheight)throwsIOException{byte[]tempBytes=pool.getBytes();GifBitmapWrapperwrapper=null;try}finally}}privateGifBitmapWrapperdecode(ImageWrappersource,intwidth,intheight,byte[]bytes)throwsIOException{}else}}privateGifBitmapWrapperdecodeStream(ImageWrappersource,intwidth,intheight,byte[]bytes)}//Decodingthegifmayfailevenifthetypematches.if(result==null){//WecanonlyresetthebufferedInputStream,sotostartfromthebeginningofthestream,weneedto//passinanewsourcecontainingthebufferedstreamratherthanthe result=decodeBitmapWrapper(forBitmapDecoder,width,}}privateGifBitmapWrapperdecodeBitmapWrapper(ImageWrappertoDecode,intwidth,intheight)throwsIOException{Resource<Bitmap>bitmapResource=bitmapDecoder.decode(toDecode,width,height);if(bitmapResource!=null){}}

首先,在ecde()方法中,又去调用了另外一个ecode()方法的重载。然后在第23行调用了ecodeteam()方法,准备从服务器返回的流当中数据。decodeteam()方法中会先从流中2个字节的数据,来判断这是GIF图还是普通的静图,如果是GIF图就调用ecodeGifWapper()方法来进行,如果是普通的静图就用调用decodeBitmapapper()方法来进行。这里只分析普通静图的实现流程,GIF图的实现有点过于复杂了,无法在本篇文章当中分析。然后来看一下decodeBitmapWrapper()方法,这里在第52行调用了bitmapDecoder.decode()方法。bitmapDecoder是一ImageBitmapDecoder对象,publicclassImageBitmapDecoderimplementsResourceDecoder<ImageWrapper,Bitmap>{privatefinalResourceDecoder<ParcelFileDescriptor,Bitmap>publicImageBitmapDecoder(ResourceDecoder<InputStream,Bitmap>{this.streamDecoder=streamDecoder;}publicResource<Bitmap>decode(ImageWrappersource,intwidth,intheight)throwsIOException{Resource<Bitmap>result=null;InputStreamis=source.getStream();if(is!=null){try}catch(IOExceptione)}}}if(result==null)if(fileDescriptor!=null){result=fileDescriptorDecoder.decode(fileDescriptor,width,}}}}代码并不复杂,在第14行先调用了source.getStream()来获取到服务器返回的InputStream,然后在第17行调用streamDecoder.decode()方法进行。streamDecode是一个StreamBitmapDecoder对象,那么再来看这个类的源码,如下所示:publicclassStreamBitmapDecoderimplementsResourceDecoder<InputStream,Bitmap>privateBitmapPoolbitmapPool; DecodeFormatdecodeFormat){this.downsampler=downsampler;this.bitmapPool=bitmapPool;} returnBitmapResource.obtain(bitmap,}}人心的时刻了,Downsampler的代码如下所示: publicBitmapdecode(InputStreamis,BitmapPoolpool,intoutWidth,intoutHeight,DecodeFormatdecodeFormat){finalByteArrayPoolbyteArrayPool=ByteArrayPool.get();finalbyte[]bytesForOptions=byteArrayPool.getBytes();finalbyte[]bytesForStream=byteArrayPool.getBytes();finalBitmapFactory.Optionsoptions=getDefaultOptions();//Usetofixthemarklimittoavoidallocatingbuffersthatfitentireimages.is,//Usetoretrieveexceptionsthrownwhile//TODO(#126):whentheframeworknolongerreturnspartiallydecodedBitmapsorprovidesawaytodetermine//ifaBitmapispartiallydecoded,considerremoving.ExceptionCatchingInputStreamexceptionStream=//Usetoread//Ensuresthatwecwaysresetafterreadinganimageheadersothatwecanstillattempttodecodethe//fullimageevenwhentheheaderdecodefailsand/oroverflowsourreadbuffer. try

intorientation=0;tryorientation=new}catch(IOExceptione)}}finallytry}catch(IOExceptione)}}}options.inTempStorage=finalint[]inDimens=getDimensions(invalidatingStream,bufferedStream,options);finalintinWidth=inDimens[0];finalintinHeight= finalintsampleSize=getRoundedSampleSize(degreesToRotate,inWidth,inHeight,outWidth,outHeight);downsampleWithSize(invalidatingStream,bufferedStream,options,pool,inWidth,inHeight,sampleSize,//BitmapFactoryswallowsexceptionsduringdecodesandinsomecaseswheninBitmapisnonnull,maycatch//andlogastacktracebutstillreturnanonnullbitmap.Toavoiddisplayingpartiallydecodedbitmaps, ExceptionCatchingInputStreamandthrowthemhere.finalExceptionstreamException=exceptionStream.getException();if(streamException!=null){

Bitmaprotated=if(downsa

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论