入行一年半的android程序员,半个月前的一个上午还在跟同事讨论发不发年终奖,下午就被裁了。找工作快两个礼拜了,连面试的都没有,更别说找个好工作了。估计再呆下去真得抑郁症了
同理,上来先喷一下,横线之后开始写内容
Glide的基本用法 1 Glide .with (this).load ("" ).into ();
Glide :: with 创建了一个RequestManagerRetriever 的实例,调用了get方法
1 2 3 4 public static RequestManager with (FragmentActivity activity ) { RequestManagerRetriever retriever = RequestManagerRetriever.get (); return retriever.get (activity); }
RequestManagerRetriever :: get 先检查一下线程,如果不在主线程,执行get方法 。 ps: 不过我记得,加载图片,是不能放在线程里的,这里却放过了线程
1 2 3 4 5 6 7 8 9 public RequestManager get (FragmentActivity activity) { if (Util.isOnBackgroundThread()) { return get (activity.getApplicationContext()); } else { assertNotDestroyed(activity); FragmentManager fm = activity.getSupportFragmentManager(); return supportFragmentGet(activity, fm); } }
RequestManager :: supportFragmentGet
从FragmentManager中取出当前页面的fragment (SupportRequestManagerFragment 这个fragment主要的功能就是同步activity的生命周期)
要是activity没有这个fragment的话,1 2 3 * 还没有的话创建一个fragment ps:这里有个问题,每次创建完之后,发一个handler,把存储在pendingSupportRequestManagerFragments中的fragment干掉了,不知道是干嘛的,猜测是避免重复创建frgment的一个手段吧
RequestManager supportFragmentGet(Context context, FragmentManager fm) { SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm); RequestManager requestManager = current.getRequestManager(); if (requestManager == null) { requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode()); current.setRequestManager(requestManager); } return requestManager; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ``` SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) { SupportRequestManagerFragment current = (SupportRequestManagerFragment ) fm.findFragmentByTag( FRAGMENT_TAG ); if (current == null) { current = pendingSupportRequestManagerFragments.get(fm); if (current == null) { current = new SupportRequestManagerFragment (); pendingSupportRequestManagerFragments .put (fm, current); fm .beginTransaction ().add (current, FRAGMENT_TAG ).commitAllowingStateLoss (); handler .obtainMessage (ID_REMOVE_SUPPORT_FRAGMENT_MANAGER , fm).sendToTarget (); } } return current ; }
接下来最主要的就是RequestManager 了
RequestManager RequestManager管理了每个acitivity上的图片请求,RequestManager 含有了activity的生命周期,并且含有一个RequestTracker这样的请求栈,这个栈是一个包含有请求(request)的arrayList
RequestManager :: load 还记得上面说到的request吗?这里就根据传入的资源去构建一个request build,这里就是构建了一个DrawableTypeRequest 。在RequestManager 中有各种load,load调用了各种from,但都调用了一个loadGeneric方法
1 2 3 public DrawableTypeRequest < Integer > load(Integer resourceId) { return (DrawableTypeRequest < Integer > ) fromResource().load(resourceId); }
RequestManager :: loadGeneric 从代码上看,主要创建了两个modelLoader 和调用了 optionsApplier.apply。(这里要打一个大大的问号了……)这段代码不是很懂,结合这注释看,总之这里创建了DrawableTypeRequest (具体过程先不管了),调用父类的load方法,也就是GenericRequestBuilder的load
1 2 3 4 5 6 7 8 9 private < T > DrawableTypeRequest < T > loadGeneric(Class < T > modelClass) { ModelLoader < T, InputStream > streamModelLoader = Glide.buildStreamModelLoader(modelClass, context); ModelLoader < T,ParcelFileDescriptor > fileDescriptorModelLoader = Glide.buildFileDescriptorModelLoader(modelClass, context); if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null ) { throw new IllegalArgumentException ("Unknown type " + modelClass + ". You must provide a Model of a type for" + " which there is a registered ModelLoader, if you are using a custom model, you must first call" + " Glide#register with a ModelLoaderFactory for your custom model class" ); } return optionsApplier.apply(new DrawableTypeRequest < T > (modelClass, streamModelLoader, fileDescriptorModelLoader, context, glide, requestTracker, lifecycle, optionsApplier)); }
GenericRequestBuilder :: into GenericRequestBuilder中的into方法,首先进行了线程的检查,等异常的处理和transform的几种缩放处理,之后统一调用了into方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public Target < TranscodeType > into(ImageView view) { Util.assertMainThread(); if (view == null ) { throw new IllegalArgumentException("You must pass in a non null View" ); } if (!isTransformationSet && view.getScaleType() != null ) { switch (view.getScaleType()) { case CENTER_CROP: applyCenterCrop(); break ; case FIT_CENTER: case FIT_START: case FIT_END: applyFitCenter(); break ; default: } } return into(glide.buildImageViewTarget(view, transcodeClass)); }
GenericRequestBuilder :: into(target) 这个方法主要看target有没有请求,若有的话清掉,在构建一个请求,添加到生命周期,并执行请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public < Y extends Target < TranscodeType >> Y into (Y target ) { Util.assertMainThread(); if (target == null ) { throw new IllegalArgumentException("You must pass in a non null Target" ); } if (!isModelSet) { throw new IllegalArgumentException("You must first set a model (try #load())" ); } Request previous = target .getRequest(); if (previous != null ) { previous.clear(); requestTracker.removeRequest(previous); previous.recycle(); } Request request = buildRequest(target ); target .setRequest(request); lifecycle.addListener(target ); requestTracker.runRequest(request); return target ; }
通过obtainRequest 去获得Request
1 2 3 private Request obtainRequest (Target < TranscodeType > target , float sizeMultiplier, Priority priority, RequestCoordinator requestCoordinator) { return GenericRequest.obtain(loadProvider, model, signature, context, priority, target , sizeMultiplier, placeholderDrawable, placeholderId, errorPlaceholder, errorId, fallbackDrawable, fallbackResource, requestListener, requestCoordinator, glide.getEngine(), transformation, transcodeClass, isCacheable, animationFactory, overrideWidth, overrideHeight, diskCacheStrategy); }
RequestTracker :: runRequest 将请求添加到请求队列中,如果没有暂停,将Request开始,并且添加到挂起的队列
1 2 3 4 5 6 7 8 public void runRequest(Request request ) { requests.add(request ); if (!isPaused) { request .begin(); } else { pendingRequests.add(request ); } }
GenericRequest :: begin begin 方法中onSizeReady() 是主要的方法,与此之外,这里还调用了target的一些方法,主要是添加占位和错误的图片,基本上就是调用了view 的设置图片,就不粘代码了。主要还是看onSizeReady
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Override public void begin () { startTime = LogTime.getLogTime(); if (model == null ) { onException(null ); return ; } status = Status.WAITING_FOR_SIZE; if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { target .getSize(this ); } if (!isComplete() && !isFailed() && canNotifyStatusChanged()) { target .onLoadStarted(getPlaceholderDrawable()); } if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Override public void onSizeReady(int width , int height ) { if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime)); } if (status != Status.WAITING_FOR_SIZE) { return ; } status = Status.RUNNING; width = Math.round (sizeMultiplier * width ); height = Math.round (sizeMultiplier * height ); ModelLoader < A, T > modelLoader = loadProvider.getModelLoader(); final DataFetcher < T > dataFetcher = modelLoader.getResourceFetcher(model, width , height ); if (dataFetcher == null ) { onException(new Exception("Failed to load model: \'" + model + "\'" )); return ; } ResourceTranscoder < Z, R > transcoder = loadProvider.getTranscoder(); if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime)); } loadedFromMemoryCache = true ; loadStatus = engine.load(signature, width , height , dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this ); loadedFromMemoryCache = resource != null ; if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime)); } }
主要的是这一段
1 loadStatus = engine.load(signature, width , height , dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this );
调用loadFromCache从内存加载,若返回值为空再次从活动的资源中加载,若再次为空查看jobs是否提交过任务,若没有提交则创建EngineRunnable,并将任务提交到engineJob中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public < T,Z,R > LoadStatus load(Key signature, int width , int height , DataFetcher < T > fetcher, DataLoadProvider < T, Z > loadProvider, Transformation < Z > transformation, ResourceTranscoder < Z, R > transcoder, Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) { Util.assertMainThread(); long startTime = LogTime.getLogTime(); final String id = fetcher.getId(); EngineKey key = keyFactory.buildKey(id, signature, width , height , loadProvider.getCacheDecoder(), loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(), transcoder, loadProvider.getSourceEncoder()); EngineResource < ?>cached = loadFromCache(key , isMemoryCacheable); if (cached != null ) { cb.onResourceReady(cached); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from cache" , startTime, key ); } return null ; } EngineResource < ?>active = loadFromActiveResources(key , isMemoryCacheable); if (active != null ) { cb.onResourceReady(active); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from active resources" , startTime, key ); } return null ; } EngineJob current = jobs.get (key ); if (current != null ) { current.addCallback(cb); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Added to existing load" , startTime, key ); } return new LoadStatus(cb, current); } EngineJob engineJob = engineJobFactory.build(key , isMemoryCacheable); DecodeJob < T,Z, R > decodeJob = new DecodeJob < T, Z,R > (key , width , height , fetcher, loadProvider, transformation, transcoder, diskCacheProvider, diskCacheStrategy, priority); EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority); jobs.put(key , engineJob); engineJob.addCallback(cb); engineJob.start(runnable); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Started new load" , startTime, key ); } return new LoadStatus(cb, engineJob); }
看一下EngineRunnable的run方法,对资源进行解码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Override public void run() { if (isCancelled) { return ; } Exception exception = null ; Resource < ?> resource = null ; try { resource = decode(); } catch (Exception e) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Exception decoding" , e); } exception = e; } if (isCancelled) { if (resource != null ) { resource.recycle(); } return ; } if (resource == null ) { onLoadFailed(exception ); } else { onLoadComplete(resource); } }
是从缓存中拿到还是从资源中拿到,应该在decodeFromCache指的是缓存,FromSource拉取资源。缓存是通过DiskLruCache进行获取的。继续跟到decodeFromSource中,最后到了decodeSource这个方法中
1 2 3 4 5 6 7 private Resource < ?>decode() throws Exception { if (isDecodingFromCache()) { return decodeFromCache(); } else { return decodeFromSource(); } }
最主要的是fetcher的loadData方法,从注释上来看,fetcher是一个用于加载资源的接口,实现这个接口的类很多, 取一个最常用的 HttpUrlFetcher 试着去看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private Resource < T > decodeSource() throws Exception { Resource < T > decoded = null ; try { long startTime = LogTime.getLogTime(); final A data = fetcher.loadData(priority); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Fetched data" , startTime); } if (isCancelled) { return null ; } decoded = decodeFromSourceData(data); } finally { fetcher.cleanup(); } return decoded; }
最后关注了一下loadDataWithRedirects中的联网请求
HttpUrlFetcher ::loadDataWithRedirects 在代码中使用了
1 2 3 4 5 6 7 8 9 10 11 12  基本上glide的流程都走了一遍,但每个模块都没有深入的研究,有时间再去看看几个有疑问地方的代码实现。 这里贴一张网络图片,记录一下glide的总体设计  --- ### Glide 怎么加载的okhttp作为网络请求的 ? 在一个app,为了保持网络请求的一致性,通常也会把Glide的数据加载换成与本来项目中的网络请求一致的框架,glide也支持这些,这是官方的文档 https://github.com/bumptech/glide/wiki/Integration-Libraries。 比如我用的ide是android studio, 当我添加了这个依赖
dependencies { compile ‘com.github.bumptech.glide:okhttp-integration:1.4.0@aar’ //compile ‘com.squareup.okhttp:okhttp:2.2.0’ }
1 gradle会自动在```AndroidManifest.xml```中添加一下这段标签,ant 或者 maven就得手动添加
1 在Glide创建RequestManager时,RequestManager的构造方法会调用Glide的```get```方法。 这里读取了ManifestParser的标签,去找到```GlideModule```标签对应的```android:name
在代码指定的是
1 2 3 最后通过```ManifestParser::parseModule()```去创建一个GlideModule, 这里实现GlideModule接口的是OkHttpGlideModule 在for循环中, 调用了applyOptions方法,但是什么都没干,最后调用了registerComponents方法,创建了一个OkHttpUrlLoader.Factory(), 去注册一个 GenericLoaderFactory loaderFactory。在上面看的源码中,看不太懂的modeloader()那里, 正好获得了这个OkHttpUrlLoader。
public static Glide get(Context context) { if (glide == null) { synchronized(Glide.class) { if (glide == null) { Context applicationContext = context.getApplicationContext(); List < GlideModule > modules = new ManifestParser(applicationContext).parse();
GlideBuilder builder = new GlideBuilder(applicationContext);
for (GlideModule module: modules) {
module.applyOptions(applicationContext, builder);
}
glide = builder.createGlide();
for (GlideModule module: modules) {
module.registerComponents(applicationContext, glide);
}
}
}
}
return glide;
}
private static GlideModule parseModule(String className) { Class < ?>clazz; try { clazz = Class.forName(className); } catch(ClassNotFoundException e) { throw new IllegalArgumentException(“Unable to find GlideModule implementation”, e); }
Object module;
try {
module = clazz.newInstance();
} catch(InstantiationException e) {
throw new RuntimeException("Unable to instantiate GlideModule implementation for " + clazz, e);
} catch(IllegalAccessException e) {
throw new RuntimeException("Unable to instantiate GlideModule implementation for " + clazz, e);
}
if (! (module instanceof GlideModule)) {
throw new RuntimeException("Expected instanceof GlideModule, but found: " + module);
}
return (GlideModule) module;
}
1 2 3 4 5 6 7 8 9 ``` public class OkHttpGlideModule implements GlideModule {@Override public void applyOptions(Context context, GlideBuilder builder) { } @Override public void registerComponents(Context context, Glide glide) { glide.register(GlideUrl.class , InputStream.class , new OkHttpUrlLoader.Factory()); } }
1 2 3 4 5 6 7 8 9 10 11 public class OkHttpGlideModule implements GlideModule {@Override public void applyOptions(Context context, GlideBuilder builder) { } @Override public void registerComponents(Context context, Glide glide) { glide.register(GlideUrl.class , InputStream.class , new OkHttpUrlLoader.Factory()); } }
Glide 怎么实现对生命周期的处理? Glide 使用创建一个fragment的方式,监视了activity的生命周期。
1 2 3 4 5 6 FragmentManager fm = getSupportFragmentManager(); FeedFragment current = (FeedFragment) fm.findFragmentByTag(FRAGMENT_TAG); if (current == null ){ current = new FeedFragment(); fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss(); }
这样就可以同步activity的生命周期
当启动一个应用 Activity onCreate: Fragment onAttach: Fragment onCreate: Fragment onCreateView: Fragment onActivityCreated Fragment onStart: Activity onStart: Activity onResume: Fragment onResume:
按下home Fragment onPause: Activity onPause: Fragment onStop: Activity onStop:
重新唤醒 Fragment onStart: Activity onStart: Activity onResume: Fragment onResume:
退出应用 Fragment onPause: Activity onPause: Fragment onStop: Activity onStop: Fragment onDestroyView: Fragment onDestroy: Fragment onDetach: Activity onDestroy:
Glide怎么判断的图片大小? 根据上面的流程,我定位到了
:: begin```方法 1 2 这里先对宽高,进行判断,若不合法,走target的getSize, getSize也会对宽高判断,失败了就会抛出异常(throw new IllegalArgumentException("You cannot set the tag id more than once or change" + " the tag id after the first request has been made");)我准备沿着代码逻辑向前走,看看是哪里来的overrideWidth。
@Override public void begin() { startTime = LogTime.getLogTime(); if (model == null) { onException(null); return; }
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
1 在```GenericRequestBuilder```中对宽和高进行初始化,初始化的值是-1 ,发现以下这些类都调用了对宽高进行了赋值,这些类都是GenericRequestBuilder的子类,都会调用```GenericRequestBuilder :: override()
方法
在
:: loadGeneric```创建了DrawableTypeRequest, DrawableTypeRequest是DrawableRequestBuilder的子类, DrawableRequestBuilder这个继续调用父类的构造。最后找到```Glide :: 的buildImageViewTarget```到```ImageViewTargetFactory :: buildTarget```拿 1 2 3 DrawableImageViewTarget分析,一直跟到ViewTarget的构造, 这里创建了一个```SizeDeterminer```,最上面的代码```target.getSize(this);```实际上调用了就是这个SizeDeterminer的getSize.看了一圈, 又回到了GenericRequest类; 所以,在GenericRequest的begin中,当指定了override时, 直接调用onSizeReady;没有则调用了target(target理解成view就好)getSize,看当前的view是否已经有宽高了,若没有则去监听view的宽高,再去调用onSizeReady #### ViewTarget :: getSize
public void getSize(SizeReadyCallback cb) { int currentWidth = getViewWidthOrParam(); int currentHeight = getViewHeightOrParam(); if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) { cb.onSizeReady(currentWidth, currentHeight); } else { // We want to notify callbacks in the order they were added and we only expect one or two callbacks to // be added a time, so a List is a reasonable choice. if (!cbs.contains(cb)) { cbs.add(cb); } if (layoutListener == null) { final ViewTreeObserver observer = view.getViewTreeObserver(); layoutListener = new SizeDeterminerLayoutListener(this); observer.addOnPreDrawListener(layoutListener); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ### Glide 设置缩略图之后回莫名其妙的‘闪’一下 在加载一个列表页的时候,通常会添加一个占位图,并设置crossfade这个属性,能让显示更加平稳,显示效果也比较好看。但出现一个问题。图片加载的时候, 图片不仅仅回做透明度的变化,并且大小也改变了, 显示效果页比较难看。 #### GlideDrawableImageViewTarget::onResourceReady 最终图片加载会走到这里,中间一堆注释先不看(看不懂) 
当未完成未失败的情况下,加载了placeholder,这时候view显示的占位图试placeholder, currentDrawable却是占位图的, 但进行动画之后glide回按照imageview的宽高裁剪图片,这样一来,必然会出现闪一下的这种情况了
Glide本地缓存是什么样的?