转载地址:http://www.jianshu.com/p/7125feef0ddf
Glide简介
Glide是Google推荐的一套快速高效的图片加载框架,作者是bumptech,功能强大且使用方便,实际的android应用开发中,有不少的开发者在使用它,今天,老衲就带大家来讲解下Glide的使用及实现的逻辑流程。
Glide的使用
Glide的使用与前一篇的Picasso类似,都是链式调用,极其方便。但是,与其他的图片加载框架不同的是,Glide支持GIF的加载与解码。这是该框架的一个亮点,以下为常用API
//设置默认和出错时的图片 Glide.with(this).load(url).placeholder(resId).error(resId).into(mImageView) //普通的图片加载 Glide.with(this).load(url).into(mImageView); //可理解为加载动态图的第一帧的Bitmap,比如Gif Glide.with(this).load(url).asBitmap().into(imageView); //GIF加载,URL指向的资源必须是gif,如果是普通图片则不显示。 //相反,如果指向正确但没有执行asGif方法,则只是作为普通图片展示 Glide.with(this).asGif().load(url).into(mImageView); //缩略图的加载 Glide.with(yourFragment).load(yourUrl).thumbnail(0.1f).into(yourView)
Glide的核心思想:
第一条是多数人认可的观点,其他则是老衲自己在分析源码时对该框架的一些感悟。如有不对请指出
- 对象池:
Glide原理的核心是为bitmap维护一个对象池。对象池的主要目的是通过减少大对象内存的分配以重用来提高性能。对象池的概念参见对象池的使用- 生命周期绑定:
图片的加载任务会与activity或者Fragment的生命周期绑定,当界面执行onStop的使用自动暂定,而当执行onStart的时候又会自动重新开启,同样的,动态Gif图的加载也是如此,以用来节省电量,同时Glide会对网络状态做监听,当网络状态发生改变时,会重启失败的任务,以减少任务因网络连接问题而失败的概率。- 预览图的使用
为加快加载速度,提高体验,优先加载预览图- AbsListView内图片的预加载:
Glide的代码流程分析
按照惯例,首先介绍一下业务逻辑中需要用到的类。有印象即可
RequestManager
Glide用来管理和开始请求的类,实现了LifecycleListener接口并重写了如下方法,可以使用Activity和Fragment的生命周期事件机制的开启,停止及重启请求任务。
/** * 开始图片加载请求,一般在Activity或者Fragment的onStart方法内执行,用来重启失败或暂停的任务。 */ @Override public void onStart() { resumeRequests(); } /** * 暂停图片加载请求,一般在Activity或Fragment的onStop方法内执行,用来暂停任务。 */ @Override public void onStop() { pauseRequests(); } /** * 取消正在执行的请求,以及释放已完成请求的资源。 */ @Override public void onDestroy() { requestTracker.clearRequests(); }
RequestManagerFragment
没有视图的fragment,简单的来讲,就是在每一个Activity或者Fragment上又添加了一个Fragment,该Fragment没有View,仅仅用来存储RequestManager并管理Glide请求
RequestManagerRetriever
用来创建并从Activity或者Fragment检索已存在的RequestManager
Engine
负责开始加载任务,以及管理活跃的,已缓存的资源
BitmapPool(bitmap对象的缓存池)
Bitmap内存池,用来复用对象
LruBitmapPool//基于LruPoolStrategy策略的BitmapPool BitmapPoolAdapter//该实现类拒绝了对象的复用,get方法总是返回null
LruPoolStrategy
对象池内对象的匹配策略,根据不同的标准,有如下三种匹配策略
//校验bitmap内存大小和图片格式,内部的实现基于数组 1. SizeConfigStrategy //仅要求bitmap尺寸完全匹配,内部的实现基于HashMap 2. AttributeStrategy //校验bitmap尺寸和图片格式,内部实现基于TreeMap 3. SizeStrategy
RequestTracker
用来跟踪,取消和重启正在进行中,或者已完成,失败的请求
ConnectivityMonitor
网络状态改变的监听,本质是一个BroadcastReceiver,值得一提的是,该类也是LifecycleListener的实现类,所以,也会有onStart,onStop的方法,而内部的逻辑,就是对网络状态监听广播的注册于反注册
//注册网络状态改变的监听广播 private void register() { if (isRegistered) { return; } isConnected = isConnected(context); context.registerReceiver(connectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); isRegistered = true; } //取消注册 private void unregister() { if (!isRegistered) { return; } context.unregisterReceiver(connectivityReceiver); isRegistered = false; } //是否有网络链接 private boolean isConnected(Context context) { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); return networkInfo != null && networkInfo.isConnected(); } @Override public void onStart() { register(); } @Override public void onStop() { unregister(); }
Resource
支持复用及池存储功能的特殊类型的接口,以下是其部分实现类
EngineResource //支持引用计数的Resource BitmapResource //Bitmap的包装类 DrawableResource //抽象类,根据ConstantState返回一个依赖于自身ConstantState的drawable的副本 BitmapDrawableResource //BitmapDrawable的包装类 GifDrawableResource //GifDrawable的包装类
Target:
LifecycleListener接口的子类,Glide用来加载资源并在加载时通知相关声明周期事件的接口。ViewTarget是它的抽象实现类。典型的生命周期是onLoadStarted -> onResourceReady or onLoadFailed -> onLoadCleared,然而并不保证一定按照此顺序执行,比如:如果资源已经在内存中,则onLoadStarted就不会被调用,同样的,如果Target如果永远不被清除,则onLoadCleared永远不会被调用。
//加载开始时调用 void onLoadStarted(@Nullable Drawable placeholder); //加载失败是调用 void onLoadFailed(@Nullable Drawable errorDrawable); //加载结束时调用 void onResourceReady(R resource, Transition<? super R> transition); //加载任务取消并且资源被释放时调用 void onLoadCleared(@Nullable Drawable placeholder); //取回目标大小,Callback的实现类为SizeDeterminer,在ViewTarget.class中 void getSize(SizeReadyCallback cb);
ViewTarget
加载资源的基类。Target的部分实现类。根据参数的类型,有不同的实现方法,并能通过ViewTreeObserver.OnDrawListener来决定View的大小。
在需要检测任意涉及到复用View的ViewGroup时(比如listview),该类用setTag方法来存储一些标志,当检测到复用时,之前的加载任务和对应的资源文件会被取消或复用。ImageViewTarget:在ImageView中展示图片的基类,有如下两个子类 DrawableImageViewTarget:当参数是drawable的使用使用 //核心方法 @Override protected void setResource(Bitmap resource) { view.setImageBitmap(resource); } BitmapImageViewTarget:当参数是bitmap 的时候使用 //核心方法 @Override protected void setResource(Bitmap resource) { view.setImageBitmap(resource); }
LifecycleListener
Fragment和Activity生命周期方法的监听类,主要用来监听onStart,onStop,onDestroy三个方法。实现类如下RequestTracker
RequestManager:负责监听Fragment和Activity中对应的方法 @Override public void onStart() { resumeRequests(); //重启暂停或者失败的任务 targetTracker.onStart(); } @Override public void onStop() { pauseRequests(); //暂停正在执行的任务 targetTracker.onStop(); } DefaultConnectivityMonitor:负责网络状态监听广播的注册于反注册 @Override public void onStart() { register(); } @Override public void onStop() { unregister(); } BaseTarget:空实现,真正的实现者是其子类ImageViewTarget,用来开始与暂停动画 @Override public void onStart() { if (animatable != null) { animatable.start(); } } @Override public void onStop() { if (animatable != null) { animatable.stop(); } } TargetTracker:该类调用的,其实是Target类中对应的方法 @Override public void onStart() { for (Target<?> target : Util.getSnapshot(targets)) { target.onStart(); } } @Override public void onStop() { for (Target<?> target : Util.getSnapshot(targets)) { target.onStop(); } } RequestFutureTarget:空实现,忽略 NullConnectivityMonitor:空实现,忽略
DataFetcher
数据提取的抽象接口,根据资源的来源有不同的实现,例如
HttpUrlFetcher //加载网络图片数据 AssetPathFetcher //加载Asset图片数据 LocalUriFetcher //加载本地图片数据 ThumbFetcher //加载MediaStore中的缩略图数据
DiskCacheStrategy
缓存策略的抽象类,只有Glide提供的固定的几个对象,分别对应不同的策略
ALL:远程数据同时缓存Data和Resource,本地数据仅缓存Resource NONE:不缓存任何数据 DATA:解码之前直接将数据写入硬盘 RESOURCE:解码之后写入硬盘 AUTOMATIC:默认策略,根据DataFetcher以及ResourceEncoder的编码策略(EncodeStrategy)智能选择
Glide图片加载的业务流程
1. Glide的初始化
public static Glide get(Context context) { if (glide == null) { synchronized (Glide.class) { if (glide == null) { Context applicationContext = context.getApplicationContext(); //查找manifest文件中注册的懒加载的配置信息,下面会介绍到 List<GlideModule> modules = new ManifestParser(applicationContext).parse(); GlideBuilder builder = new GlideBuilder(applicationContext); for (GlideModule module : modules) { module.applyOptions(applicationContext, builder); } //创建Glide实例 glide = builder.createGlide(); for (GlideModule module : modules) { module.registerComponents(applicationContext, glide.registry); } } } } return glide; }
创建Glide实例对象
Glide createGlide() { //依赖于优先级的线程池,用来执行Glide的加载,解码和转换任务(当从缓存中没有找到对应的对象时) //线程数量取决于当前唤醒的CPU核数,而不是CPU的总数 if (sourceExecutor == null) { sourceExecutor = GlideExecutor.newSourceExecutor(); } //依赖于优先级的线程池,用来执行Glide的加载,解码和转换任务(当从缓存中有对应的对象时) //线程数量为1 if (diskCacheExecutor == null) { diskCacheExecutor = GlideExecutor.newDiskCacheExecutor(); } //内存大小的计算器,计算结果取决于一些常量和当前设备的信息(宽,高,像素密度),最后会与介绍 if (memorySizeCalculator == null) { memorySizeCalculator = new MemorySizeCalculator.Builder(context).build(); } //网络活动监视器,用来检测网络状态 if (connectivityMonitorFactory == null) { connectivityMonitorFactory = new DefaultConnectivityMonitorFactory(); } //bitmap对象池,用来存储bitmap对象 if (bitmapPool == null) { //3.0以上使用基于LRU算法的bitmap对象池 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { int size = memorySizeCalculator.getBitmapPoolSize(); bitmapPool = new LruBitmapPool(size); } else { bitmapPool = new BitmapPoolAdapter(); } } //基于LRU算法的数组缓存池 if (arrayPool == null) { arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes()); } if (memoryCache == null) { memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize()); } //硬盘缓存 if (diskCacheFactory == null) { diskCacheFactory = new InternalCacheDiskCacheFactory(context); } //负责开启加载任务以及管理活跃的或者缓存的图片资源 if (engine == null) { engine = new Engine(memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor); } return new Glide(context, engine, memoryCache, bitmapPool, arrayPool, connectivityMonitorFactory, logLevel, defaultRequestOptions.lock()); }
Glide真正的构造方法
Glide(Context context, Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, ArrayPool arrayPool, ConnectivityMonitorFactory connectivityMonitorFactory, int logLevel, RequestOptions defaultRequestOptions) { this.engine = engine; this.bitmapPool = bitmapPool; this.arrayPool = arrayPool; this.memoryCache = memoryCache; this.connectivityMonitorFactory = connectivityMonitorFactory; //图片的加载格式(ARGB_8888或RGB_565),默认ARGB_8888,判断规则如下 //如果支持透明或者使用了透明则使用ARGB_8888 //如果不支持透明则使用ARGB_565 DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT); //BitmapPool的预填充器,最后面有介绍 bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat); Resources resources = context.getResources(); //从给定的inputstream中解码图片 Downsampler downsampler = new Downsampler(resources.getDisplayMetrics(), bitmapPool, arrayPool); //Gif图片资源的解码器 ByteBufferGifDecoder byteBufferGifDecoder = new ByteBufferGifDecoder(context, bitmapPool, arrayPool); ... //包含了加载图片资源所需要的类,比如Registry,Engine,等等 glideContext = new GlideContext(context, registry, imageViewTargetFactory, defaultRequestOptions, engine, this, logLevel); }
2. 创建请求管理器RequestManager
首先根据context的类型,拿到FragmentManager
public RequestManager get(Context context) { if (context == null) { ... } else if (Util.isOnMainThread() && !(context instanceof Application)) { if (context instanceof FragmentActivity) { //FragmentActivity return get((FragmentActivity) context); } else if (context instanceof Activity) { //Activity return get((Activity) context); } else if (context instanceof ContextWrapper) { //如果不属于以上两种,则递归查找父类Context return get(((ContextWrapper) context).getBaseContext()); } } //返回ApplicationManager return getApplicationManager(context); } ...//get()中会调用fragmentGet方法 @TargetApi(Build.VERSION_CODES.HONEYCOMB) RequestManager fragmentGet(Context context, android.app.FragmentManager fm, android.app.Fragment parentHint) { //获取RequestManagerFragment RequestManagerFragment current = getRequestManagerFragment(fm, parentHint); //获取RequestManagerFragment对应的RequestManager(请求管理器) RequestManager requestManager = current.getRequestManager(); if (requestManager == null) { requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode()); current.setRequestManager(requestManager); } return requestManager; }
RequestManagerRetriever中使用两个map用来分别存放RequestManagerFragment和SupportRequestManagerFragment,Key都是FragmentManager。说白了就是每一个Activity或者FragmentActivity都有一个唯一的FragmentManager,通过这个FragmentManager作为key就可以找到该Activity对应的RequestManagerFragment,Glide就是通过这个Fragment用来实现在生命周期中图片加载的控制,比如Paused状态在暂停加载,在Resumed的时候又自动重新加载
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm , android.app.Fragment parentHint) { RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG); if (current == null) { //pendingRequestManagerFragments是一个 current = pendingRequestManagerFragments.get(fm); if (current == null) { current = new RequestManagerFragment(); current.setParentFragmentHint(parentHint); pendingRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss(); handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget(); } } return current; }
2. 填充资源路径(网络路径或本地路径)
/** * 利用默认的请求参数创建RequestBuilder * 默认的参数包括,是否启动硬盘缓存,优先级,错误及加载中的默认图片等 * 详情请看BaseRequestOptions.java类 * @return A new request builder for loading a { Drawable} using the given model. */ public RequestBuilder<Drawable> load(@Nullable Object model) { return asDrawable().load(model); }
3. 设置要加载图片的Target(ImageView)
public Target<TranscodeType> into(ImageView view) { Util.assertMainThread(); Preconditions.checkNotNull(view); if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null) { ... return into(context.buildImageViewTarget(view, transcodeClass)); } public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) { Util.assertMainThread(); Preconditions.checkNotNull(target); if (!isModelSet) { //是否设置了url ... } //获取该Target之前的请求任务(如果有的话) Request previous = target.getRequest(); //如果有,取消该Target之前所有的任务并释放资源(例如bitmap)以备复用 if (previous != null) { requestManager.clear(target); } requestOptions.lock(); //创建请求 Request request = buildRequest(target); target.setRequest(request); // TODO:下载图片的任务 requestManager.track(target, request); return target; }
Glide的缩略图加载思想,为了更快的展示图片,一般来说,一张图片的加载任务分为全尺寸图片和缩略图两部分,因为缩略图更小,所以一般来说相对于全尺寸图片会加载更快,但不绝对,如果缩略图先加载完则先展示缩略图,然后等全尺寸图片加载完成后再加载全尺寸图片,但是,如果全尺寸图片先于缩略图下载完成,则缩略图则不会展示。
private Request buildRequestRecursive(Target<TranscodeType> target, @Nullable ThumbnailRequestCoordinator parentCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight) { //默认的该thumbnailBuilder对象为null,除非手动调用RequestBuilder类的thumbnail方法, //否则该if代码永远不会执行 if (thumbnailBuilder != null) { // Recursive case: contains a potentially recursive thumbnail request builder. if (isThumbnailBuilt) { throw new IllegalStateException("You cannot use a request as both the main request and a thumbnail, consider using clone() on the request(s) passed to thumbnail()"); } TransitionOptions<?, ? super TranscodeType> thumbTransitionOptions = thumbnailBuilder.transitionOptions; if (DEFAULT_ANIMATION_OPTIONS.equals(thumbTransitionOptions)) { thumbTransitionOptions = transitionOptions; } //缩略图权限 Priority thumbPriority = thumbnailBuilder.requestOptions.isPrioritySet() ? thumbnailBuilder.requestOptions.getPriority() : getThumbnailPriority(priority); //缩略图宽高 int thumbOverrideWidth = thumbnailBuilder.requestOptions.getOverrideWidth(); int thumbOverrideHeight = thumbnailBuilder.requestOptions.getOverrideHeight(); //宽高校验 if (Util.isValidDimensions(overrideWidth, overrideHeight) && !thumbnailBuilder.requestOptions.isValidOverride()) { thumbOverrideWidth = requestOptions.getOverrideWidth(); thumbOverrideHeight = requestOptions.getOverrideHeight(); } //缩略图请求协调器,用来同时协调缩略图和原始图片的请求 ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator); //原始图片请求 Request fullRequest = obtainRequest(target, requestOptions, coordinator, transitionOptions, priority, overrideWidth, overrideHeight); isThumbnailBuilt = true; // Recursively generate thumbnail requests //递归生成缩略图请求 Request thumbRequest = thumbnailBuilder.buildRequestRecursive(target, coordinator, thumbTransitionOptions, thumbPriority, thumbOverrideWidth, thumbOverrideHeight); isThumbnailBuilt = false; coordinator.setRequests(fullRequest, thumbRequest); return coordinator; } else if (thumbSizeMultiplier != null) { //根据指定缩放系数加载缩略图 ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator); Request fullRequest = obtainRequest(target, requestOptions, coordinator, transitionOptions, priority, overrideWidth, overrideHeight); BaseRequestOptions<?> thumbnailOptions = requestOptions.clone().sizeMultiplier(thumbSizeMultiplier); Request thumbnailRequest = obtainRequest(target, thumbnailOptions, coordinator, transitionOptions, getThumbnailPriority(priority), overrideWidth, overrideHeight); coordinator.setRequests(fullRequest, thumbnailRequest); return coordinator; } else { // 只加载原始图片 return obtainRequest(target, requestOptions, parentCoordinator, transitionOptions, priority, overrideWidth, overrideHeight); } }
至此,Glide的流程就分析完了,但是!!!和我们之前了解到的Picasso或者Imageloader等图片加载框架不同,我们完全没有看到他的网络请求已经缓存查找的业务逻辑,那这段逻辑究竟在哪呢。我们继续往下看
图片的加载
在上面的最后一步,buildRequestRecursive方法,不管是否有缩略图,我们都会返回一个Request,这个Request会和Target一起被RequestManager接收
void track(Target<?> target, Request request) { targetTracker.track(target); //runRequest最终执行的是Request的begin方法,下面以SingleRequest为例讲解下流程 requestTracker.runRequest(request); } @Override public void begin() { stateVerifier.throwIfRecycled(); startTime = LogTime.getLogTime(); //如果图片的来源没有设置,则加载失败,来源是指网络,本地硬盘或者资源文件等。 if (model == null) { if (Util.isValidDimensions(overrideWidth, overrideHeight)) { width = overrideWidth; height = overrideHeight; } ... onLoadFailed(new GlideException("Received null model"), logLevel); return; } status = Status.WAITING_FOR_SIZE; //如果Target的宽高已经获取并且合法,则开始进行下一步 if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { //手动获取Target的宽高 target.getSize(this); } if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); } if (Log.isLoggable(TAG, Log.VERBOSE)) { ... } } @Override public void onSizeReady(int width, int height) { stateVerifier.throwIfRecycled(); ... status = Status.RUNNING; //计算缩略图的尺寸 float sizeMultiplier = requestOptions.getSizeMultiplier(); this.width = Math.round(sizeMultiplier * width); this.height = Math.round(sizeMultiplier * height); ... //加载任务 loadStatus = engine.load(glideContext, model, requestOptions.getSignature(), this.width, this.height, requestOptions.getResourceClass(), transcodeClass, priority, requestOptions.getDiskCacheStrategy(), requestOptions.getTransformations(), requestOptions.isTransformationRequired(), requestOptions.getOptions(), requestOptions.isMemoryCacheable(), this); ... }
加载资源
Glide图片的资源加载与其他图片加载框架的加载逻辑类似,都是按照内存,硬盘及网络的顺序来加载图片,但是Glide得加载又稍显不同,他是的逻辑如下:
/** * 1. 检查内存缓存 * 2. 检查最近的活跃资源 * 3. 检查最近的加载任务 * 活跃资源指的是那些不止一次被加载并没有进行过资源释放的图片,一旦被释放, * 那么该资源则会从近期活跃资源中删除并进入到内存缓存中, * 但是如果该资源再次从内存缓存中读取,则会重新添加到活跃资源中 */ 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, Options options, boolean isMemoryCacheable, ResourceCallback cb) { Util.assertMainThread(); long startTime = LogTime.getLogTime(); //内存缓存的唯一键值 EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); //首先从缓存中查找 EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); if (cached != null) { cb.onResourceReady(cached, DataSource.MEMORY_CACHE); ... return null; } //如果缓存中没有找到,则去活跃资源中加载 //memCache中该bitmap则会被remove掉bitmap并进入activeResource中 EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); if (active != null) { cb.onResourceReady(active, DataSource.MEMORY_CACHE); ... } return null; } //如果该任务之前已经在队列中,则添加新的callback,然后返回 EngineJob current = jobs.get(key); if (current != null) { current.addCallback(cb); ... return new LoadStatus(cb, current); } //如果是新的加载任务,先创建EngineJob和DecodeJob,然后开始任务 EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable); DecodeJob<R> decodeJob = 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); ... return new LoadStatus(cb, engineJob); } /** * DecodeJob 类中run方法的实现,DecodeJob是一个Runnable的实现类 * 该方法的作用是, * 1.确定数据的加载来源(Resource,Data,Source) * 2.创建对应来源的DataFetcherGenerator * 3.执行DataFetcherGenerator */ 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); } } /** * 根据当前阶段获取下一阶段 * Data和Resource的区别: * Data:原始的图片(或gif)数据 * Resource:经过处理(旋转,缩放)后的数据 * 该方法的大致逻辑如下 * 1.如果是初始状态,则判断是否解码已缓存的Resource,true是解码Resource。 * false的话则会通过递归进入第二个判断分支 * 2.判断是否解码已缓存的Data,true是解码Data * false的话则会通过递归进入第三个判断分支 * 3.该阶段则需要从数据源去解码。 * 简单的来说,就是根据Resource--->Data--->source的顺序去解码加载数据 * 该阶段Stage的确定,影响着下一阶段DataFetcherGenerator相应子类的实例创建 */ private Stage getNextStage(Stage current) { switch (current) { case INITIALIZE: return diskCacheStrategy.decodeCachedResource() ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE); case RESOURCE_CACHE: return diskCacheStrategy.decodeCachedData() ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE); case DATA_CACHE: return Stage.SOURCE; case SOURCE: case FINISHED: return Stage.FINISHED; default: throw new IllegalArgumentException("Unrecognized stage: " + current); } } /** * 根据不同的阶段创建不同的DataFetcherGenerator,该类使用已注册的ModelLoaders和Model * 来生成一系列的DataFetcher。有如下实现类 * DataFetcherGenerator:经过处理的资源数据缓存文件(采样转换等处理) * ResourceCacheGenerator:未经处理的资源数据缓存文件 * SourceGenerator:源数据的生成器,包含了根据来源创建的ModelLoader和Model(文件路径,URL...) */ 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); } } /** * 根据不同的状态来选择并执行生成器 * 从当前Generator 获取数据,如果获取成功则直接回调onDataFetcherReady, * 如果失败则通过reschedule重新调度 */ private void runGenerators() { ... boolean isStarted = false; while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) { stage = getNextStage(stage); currentGenerator = getNextGenerator(); if (stage == Stage.SOURCE) { reschedule(); return; } } // 加载失败 if ((stage == Stage.FINISHED || isCancelled) && !isStarted) { notifyFailed(); } }
如果是首次加载一张图片资源,最终会来到SourceGenerator的startNext来执行。
/** * SourceGenerator * DataFetcher的简介:Fetcher的意思是抓取,所以该类可以称为数据抓取器 * 作用就是根据不同的数据来源(本地,网络,Asset等) * 以及读取方式(Stream,ByteBuffer等)来提取并解码数据资源,实现类如下 * AssetPathFetcher:加载Asset数据 * HttpUrlFetcher:加载网络数据 * LocalUriFetcher:加载本地数据 * 其他实现类... * */ @Override public boolean startNext() { ... if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) { return true; } sourceCacheGenerator = null; loadData = null; boolean started = false; //是否有更多的ModelLoader 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,并使用LoadData中的fetcher来抓取数据 loadData.fetcher.loadData(helper.getPriority(), this); } } return started; }
当走完上面的流程,接下来就是我们最熟悉的网络数据请求的模块了,因为该模块在整个的图片加载流程中并不是很重要,所以就简单介绍一下,不过有几个知识点还是比较有意思的。
/** * HttpUrlFetcher * HttpUrlFetcher的简介:网络数据抓取器,通俗的来讲就是去服务器上下载图片,支持地址重定向(最多5次) * */ @Override public void loadData(Priority priority, DataCallback<? super InputStream> callback) { long startTime = LogTime.getLogTime(); final InputStream result; try { result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders()); } catch (IOException e) { ... callback.onLoadFailed(e); return; } ... callback.onDataReady(result); } 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 { //通过URL的equals方法来比较会导致NetworkI/O开销,一般会有问题, //有兴趣的同学可以看下下面的链接或者直接阅读URL里equals方法的源码注释,一目了然 //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. } } //HttpUrlConnection下载图片 ... }
Glide的知识点
接下来,老衲带大家看下Glide中有什么我们在日常开发的时候能用得上的技术
1.线程池内线程的个数的计算方式
/** * 根据/sys/devices/system/cpu/下的文件来决定线程池内线程的数量 * 决定线程数量的不是一共得CPU核数,而是唤醒的CPU核数 * * See http://goo.gl/8H670N. */ public static int calculateBestThreadCount() { File[] cpus = null; try { File cpuInfo = new File(CPU_LOCATION); final Pattern cpuNamePattern = Pattern.compile(CPU_NAME_REGEX); cpus = cpuInfo.listFiles(new FilenameFilter() { @Override public boolean accept(File file, String s) { return cpuNamePattern.matcher(s).matches(); } }); } catch (Throwable t) { ... } int cpuCount = cpus != null ? cpus.length : 0; int availableProcessors = Math.max(1, Runtime.getRuntime().availableProcessors()); return Math.min(MAXIMUM_AUTOMATIC_THREAD_COUNT, Math.max(availableProcessors, cpuCount)); }
2. 权限判断
以网络请求的权限为例
final int res = context .checkCallingOrSelfPermission("android.permission.ACCESS_NETWORK_STATE"); final boolean hasPermission = res == PackageManager.PERMISSION_GRANTED;
Glide的懒加载配置
解析Manifest文件中注册的懒加载配置信息
public List<GlideModule> parse() { List<GlideModule> modules = new ArrayList<>(); try { ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); if (appInfo.metaData != null) { for (String key : appInfo.metaData.keySet()) { if (GLIDE_MODULE_VALUE.equals(appInfo.metaData.get(key))) { modules.add(parseModule(key)); } } } } catch (PackageManager.NameNotFoundException e) { ... } return modules; }
3. Glide给出的懒加载示例(混淆代码的话请读者自己查看GlideModule类的注释)
public class FlickrGlideModule implements GlideModule { Override public void applyOptions(Context context, GlideBuilder builder) { builder.setDecodeFormat(DecodeFormat.ALWAYS_ARGB_8888); } public void registerComponents(Context context, Glide glide) { glide.register(Model.class, Data.class, new MyModelLoader()); } }
4. Glide的预填充机制
Glide最核心的部分,就是Bitmap池的使用,
Glide类中有一个preFillBitmapPool方法,用来预填充bitmap对象池,他的注释如下/** * 作用:根据给定的尺寸预填充Bitmap对象池 * 缺陷:太多的资源释放会导致GC频繁执行,这样就失去了Glide本身存在的意义。此方法要慎重使用。 * 若太多的Bitmap被添加到对象池使其完全被填满,会导致大多数甚至全部最近被添加的bitmap被驱逐(释放) * bitmap会根据给定的尺寸的权重来分配,每一种尺寸只会填充对象池所占内存的一定比例。 * 比例的计算公式为weight / prefillWeightSum */ public void preFillBitmapPool(PreFillType.Builder... bitmapAttributeBuilders) { bitmapPreFiller.preFill(bitmapAttributeBuilders); } public void preFill(PreFillType.Builder... bitmapAttributeBuilders) { if (current != null) { current.cancel(); } //PreFillType中保存着图片的宽高,Config以及weight等信息 PreFillType[] bitmapAttributes = new PreFillType[bitmapAttributeBuilders.length]; for (int i = 0; i < bitmapAttributeBuilders.length; i++) { PreFillType.Builder builder = bitmapAttributeBuilders[i]; if (builder.getConfig() == null) { builder.setConfig(defaultFormat == DecodeFormat.ALWAYS_ARGB_8888 || defaultFormat == DecodeFormat.PREFER_ARGB_8888? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); } bitmapAttributes[i] = builder.build(); } //根据PreFillType中保存的图片信息创建预填充队列 PreFillQueue allocationOrder = generateAllocationOrder(bitmapAttributes); //创建用来填充对象池的线程对象, //该类通过Handler发布到主线程中尽量避免GC因高比例的Bitmap触发垃圾回收所导致的ANR, //通过延时减少GC线程的垃圾回收的次数 current = new BitmapPreFillRunner(bitmapPool, memoryCache, allocationOrder); handler.post(current); } // Visible for testing. PreFillQueue generateAllocationOrder(PreFillType[] preFillSizes) { //剩余内存 final int maxSize = memoryCache.getMaxSize() - memoryCache.getCurrentSize() + bitmapPool.getMaxSize(); int totalWeight = 0; //计算图片总权重 for (PreFillType size : preFillSizes) { totalWeight += size.getWeight(); } //计算每一份权重占用的字节 final float bytesPerWeight = maxSize / (float) totalWeight; Map<PreFillType, Integer> attributeToCount = new HashMap<PreFillType, Integer>(); for (PreFillType size : preFillSizes) { //根据权重计算出的图片的内存占用量 int bytesForSize = Math.round(bytesPerWeight * size.getWeight()); //根据宽高和Config计算出的图片的内存占用量 int bytesPerBitmap = getSizeInBytes(size); int bitmapsForSize = bytesForSize / bytesPerBitmap; attributeToCount.put(size, bitmapsForSize); } return new PreFillQueue(attributeToCount); }
5. DefaultConnectivityMonitor 网络状态监视器
平常的开发中使用频率还是比较高的,可以直接拿来用,完整代码请参考源码
private final BroadcastReceiver connectivityReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { boolean wasConnected = isConnected; isConnected = isConnected(context); if (wasConnected != isConnected) { listener.onConnectivityChanged(isConnected); } } }; public DefaultConnectivityMonitor(Context context, ConnectivityListener listener) { this.context = context.getApplicationContext(); this.listener = listener; } private void register() { if (isRegistered) { return; } isConnected = isConnected(context); context.registerReceiver(connectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); isRegistered = true;}private void unregister() { if (!isRegistered) { return; } context.unregisterReceiver(connectivityReceiver); isRegistered = false; } private boolean isConnected(Context context) { ConnectivityManager connectivityManager = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); return networkInfo != null && networkInfo.isConnected(); } @Override public void onStart() { register(); } @Override public void onStop() { unregister(); }