图片加载框架有很多,但是glide是google官方推荐的,而且使用起来非常的方便而且功能十分强大;
1.使用方法:
Glide.with(this).load("http://baidu.com").into(new ImageView(this));
简简单单的一句代码,就可以将url转换成图片放置在相应的控件中,很神奇。
2.源码解析:
从with方法开始:
public static RequestManager with(Activity activity) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(activity); }
先看这个RequestManager:从名字我们就可以看出,这是一个图片网络请求的管理类;
public class RequestManager implements LifecycleListener
它实现了LifecycleListener接口,这是一个与Context的生命周期绑定的接口,将request与生命周期绑定,这样就可以通过context的生命周期去操作网络请求的开始,暂停等;
再看retriever的get方法
public RequestManager get(FragmentActivity activity) { if (Util.isOnBackgroundThread()) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); FragmentManager fm = activity.getSupportFragmentManager(); return supportFragmentGet(activity, fm); } }
这里做了一个判断,如果Glide在子线程中使用或者传入的context是ApplicationContext,那么就与全局的ApplicationContext绑定,如果不是那么创建一个无界面的fragment,即SupportRequestManagerFragment,让请求和你的activity的生命周期同步;
在with方法中,主要是创建了一个requestManager的类,这个是网络请求的管理类,主要负责将所有的类的生命周期与调用其的组件的生命周期相绑定,这也是Glide与其他图片加载框架不一样的地方,它是和组件的生命周期相绑定的。
再看load方法:
load方法比较简单了,它的重载方法有很多,对应了加载图片的不同方式,可以传入String类型的值去加载,也可以通过url地址去加载,还可以通过传入本地文件去加载,但最后都会返回一个DrawableTypeRequest(继承了DrawableRequestBuilder);这个类就是支持链式调用的类,主要是进行一些初始化的操作;
最后看Glide的最核心的方法,into:
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; //$CASES-OMITTED$ default: // Do nothing. } } return into(glide.buildImageViewTarget(view, transcodeClass)); }
这里有三点要注意:
(1)
Util.assertMainThread();
这个方法会判断是否在主线程运行,如果不是,那么就会抛出异常,其实想想也可以想通,into方法就是将图片加载到相应的控件中,所以,就必须在主线程中运行;
(2)下面都是一些判断,如果你没有调用transform方法并且自己对图片的scaletype进行了设置,那么他会根据你的设置去加载图片;
(3)最后它调用into的方法:
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; }
其实这个代码逻辑很清晰,但是,此处有一个target不好理解,其实target就是对View的一层封装,就把他当成一个view就可以了,代码逻辑很好理解,主要就是对之前请求的彻底清除和对新请求的建立与绑定生命周期,这也就是控件复用时图片不会错位的原因;
关于新建请求是调用buildRequest的方法
private Request buildRequest(Target<TranscodeType> target) { if (priority == null) { priority = Priority.NORMAL; } return buildRequestRecursive(target, null); } private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) { if (thumbnailRequestBuilder != null) { 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()"); } // Recursive case: contains a potentially recursive thumbnail request builder. if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) { thumbnailRequestBuilder.animationFactory = animationFactory; } if (thumbnailRequestBuilder.priority == null) { thumbnailRequestBuilder.priority = getThumbnailPriority(); } if (Util.isValidDimensions(overrideWidth, overrideHeight) && !Util.isValidDimensions(thumbnailRequestBuilder.overrideWidth, thumbnailRequestBuilder.overrideHeight)) { thumbnailRequestBuilder.override(overrideWidth, overrideHeight); } ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator); Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator); // Guard against infinite recursion. isThumbnailBuilt = true; // Recursively generate thumbnail requests. Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator); isThumbnailBuilt = false; coordinator.setRequests(fullRequest, thumbRequest); return coordinator; } else if (thumbSizeMultiplier != null) { // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse. ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator); Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator); Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator); coordinator.setRequests(fullRequest, thumbnailRequest); return coordinator; } else { // Base case: no thumbnail. return obtainRequest(target, sizeMultiplier, priority, parentCoordinator); } }
这里使用了递归,判断三个if,第一个if是设置缩略图是新的情况,第二个设置缩略图是float的情况,第三个设置没有缩略图的情况;
不管是什么缩略图,都需要请求网络,而请求网络的都是调用obtainRequest方法
这个方法没什么好讲的,就是对一些网络请求参数的初始化;
回到into方法中,当新建好请求后,就会runRequest;
requestTracker.runRequest(request);
public void runRequest(Request request) { requests.add(request); if (!isPaused) { request.begin(); } else { pendingRequests.add(request); } }
在其中调用了begin
@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)); } }
首先它会计算开始的时间,然后将图片加载状态置为等待,然后会对图片大小的合法性进行判断,最后会判断图片的加载状态,如果没有加载完成而且没有加载失败,那么就开始加载图片;
我们先来看看onSizeReady()方法
@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)); } }这个方法让我们接触到glide的最核心的类,Engine类
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this);
/** * Responsible for starting loads and managing active and cached resources. */
从他的注释就可以看出,Engine就是负责开始加载图片和管理活动或缓存中的资源的引擎类;
//DiskCache.java
/** 250 MB of cache. */
int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;
String DEFAULT_DISK_CACHE_DIR = "image_manager_disk_cache";
final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());//获得可用的处理器个数
sourceService = new FifoPriorityThreadPoolExecutor(cores);
diskCacheService = new FifoPriorityThreadPoolExecutor(1);
看它的核心方法load方法:
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); }
这里首先会从缓存中加载图片,找到的话,就将他加入到活动资源中并从缓存删去,缓存中没有再从活动资源(activeResource)中找,activeResource是一个以弱引用为值的map,它是用于存储使用中的资源,之所以在内存缓存的基础上又多了这层缓存,是为了当内存不足而清除cache中的资源中,不会影响使用中的资源;
然后load方法会通过engineJobFactory获取到EngineJob对象,里面主要管理两个线程池,diskCacheService和sourceService,最后会创建一个EngineRunnable的子线程,将子线程丢到线程池中;
我们来看看这个子线程的run方法
@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); } }通过decode方法尝试读取图片资源,
private Resource> decode() throws Exception { if (isDecodingFromCache()) { return decodeFromCache(); } else { return decodeFromSource(); } }
首先会尝试从缓存中读取:
private Resource> decodeFromCache() throws Exception { Resource> result = null; try { result = decodeJob.decodeResultFromCache(); } catch (Exception e) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Exception decoding result from cache: " + e); } } if (result == null) { result = decodeJob.decodeSourceFromCache(); } return result; }
通过decodeResultFromCache方法首先尝试读取处理后的图片,如果读不到,那么就读取原图片;
如果从缓存中读取不到,那么就调用decodeFromSource方法从SourceService(处理原图片的线程池)中读取;
public Resource<Z> decodeFromSource() throws Exception { Resource<T> decoded = decodeSource(); return transformEncodeAndTranscode(decoded); }
这里做了两件事,第一去获取原图片,第二是处理原图片:
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; }
这里会调用loadData()方法去加载图片,根据DataFetcher的不同,加载方式也不同,最常用的就是从网络中加载图片,所以看看HttpUrlFetcher
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map, String> headers) throws IOException { if (redirects >= MAXIMUM_REDIRECTS) { throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!"); } else { // Comparing the URLs using .equals performs additional network I/O and is generally broken. // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html. try { if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) { throw new IOException("In re-direct loop"); } } catch (URISyntaxException e) { // Do nothing, this is best effort. } } urlConnection = connectionFactory.build(url); for (Map.Entry , String> headerEntry : headers.entrySet()) { urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue()); } urlConnection.setConnectTimeout(2500); urlConnection.setReadTimeout(2500); urlConnection.setUseCaches(false); urlConnection.setDoInput(true); // Connect explicitly to avoid errors in decoders if connection fails. urlConnection.connect(); if (isCancelled) { return null; } final int statusCode = urlConnection.getResponseCode(); if (statusCode / 100 == 2) { return getStreamForSuccessfulRequest(urlConnection); } else if (statusCode / 100 == 3) { String redirectUrlString = urlConnection.getHeaderField("Location"); if (TextUtils.isEmpty(redirectUrlString)) { throw new IOException("Received empty or null redirect url"); } URL redirectUrl = new URL(url, redirectUrlString); return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers); } else { if (statusCode == -1) { throw new IOException("Unable to retrieve response code from HttpUrlConnection."); } throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage()); } }
这里是通过android原生的HttpUrlConnection进行网络请求;
再看处理图片的方法:
private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) { long startTime = LogTime.getLogTime(); Resource<T> transformed = transform(decoded); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Transformed resource from source", startTime); } writeTransformedToCache(transformed); startTime = LogTime.getLogTime(); Resource<Z> result = transcode(transformed); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Transcoded transformed from source", startTime); } return result; }
至此,Glide加载图片流程就完成了,这里总结一下:
(1)首先,with方法是获取requestManager类,这个类的主要作用是将glide与整个组件的生命周期绑定在一起;
(2)然后,通过load方法初始化一些参数,这里传入的参数给后面的加载方式提供了依据;
(3)最后,通过into这个核心方法将图片获取并通过一系列处理后加载到相应控件中,这部分逻辑最为复杂,建立请求,然后通过Engine引擎类将请求进行统一的处理,里面开启了两个线程池(diskcacheService)磁盘缓存线程池和(SourceService)源线程池,并且进行了两层缓存,除了我们常见的内存缓存外,还进行了活动资源的缓存,其中内部的网络请求是通过android原生的httpurlConnection去完成的;