这篇文章上次修改于 2136 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
转载请标明地址 QuincySx: http://www.jianshu.com/p/eed7054e3722
这是 Glide 的第二篇,在上一篇中讲的都是大概流程,直接阅读起来可能比较困难,推荐结合源码浏览,在这一篇中就讲资源加载,所以贴上来的源码就会多一些。
public Target<TranscodeType> into(ImageView view) {
.....
//调用 (glide.buildImageViewTarget()
return into(glide.buildImageViewTarget(view, transcodeClass));
}
然后在调用以下方法 这个地方无论调什么都是生成 ViewTarget 就不细追究了
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target<Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
创建完 ViewTarget 之后调用 init()
public <Y extends Target<TranscodeType>> Y into(Y target) {
...
//在 Target 中获取请求对象
Request previous = target.getRequest();
//如果有请求对象则把它清掉
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
//重新构建新的请求对象 并设置到 target 中,添加生命周期监听
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
//开始请求资源
requestTracker.runRequest(request);
return target;
}
然后我们进去到 RequestTracker 的 runRequestra() 方法中
public void runRequest(Request request) {
//先把请求添加到队列中,然后判断这个队列是不是暂停状态,暂停的话就放到暂停列表里,不是暂停的话就开始运行
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
简单说一下 RequestTracker 请求队列,为什么这里要维护一个暂停或运行的状态呢,因为 RequestTracker 的生命周期是跟随 RequestManager 的,如果你看过 Glide 往页面中添加 Fragment 的那个步骤的话,你就会发现 RequestManager 是在 Fragment 中维护的,他同样监听这 Fragment 的显示状态,通俗点说就是一个显示的页面那么请求队列的状态就是运行,其他已不显示的页面 队列就会成为暂停状态,因为队列是监听 Fragment 的生命周期的,会动态调整每个页面请求队列的状态,已达到节省系统资源的目的。
接下来再看 request.begin() 方法
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
//更新请求的状态
status = Status.WAITING_FOR_SIZE;
//因为如果没有设置缩小 overrideWidth,overrideHeight 默认为 -1
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
//如果设置了图片缩小,并且重新设置的宽高大于0 直接调用 onSizeReady
onSizeReady(overrideWidth, overrideHeight);
} else {
//如果没有设置了图片缩小则去计算 View 本身的宽高
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 方法之前我们先看一下 ViewTager 的 getSize 方法
public void getSize(SizeReadyCallback cb) {
int currentWidth = getViewWidthOrParam();
int currentHeight = getViewHeightOrParam();
//获取View 的宽高
if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
//宽高不为0回调 onSizeReady
cb.onSizeReady(currentWidth, currentHeight);
} else {
//宽高为0 就监听 View 的绘制之前 的事件再去获得宽高,回调进行加载
// 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);
}
}
}
//查看 onSizeReady 方法
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;
//再看一下 engine.load
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));
}
}
//我们接下来看一下 engine 的 load 方法 在调用 load 方法的时候,看到最后有一个 this 参数这是一个回调接口 这个地方注意一下
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();
//根据各个请求参数生成key
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
//根据 key 在内存缓存中获取缓存
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
//如果不为 null 则调用完成的回调接口
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;
}
//jobs 是一个正在运行的任务集合,获取 key 相同的任务 防止有相同的请求正在运作
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 的时候他把 engineJob 也传了进去而他继承自
//EngineRunnable.EngineRunnableManager 这个地方记住
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
//当前请求加入 jobs 维护
jobs.put(key, engineJob);
//添加回调到 GenericRequest 的方法
engineJob.addCallback(cb);
//运行任务
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
接下来我们再看 engineJob.start(runnable); 方法
public void start(EngineRunnable engineRunnable) {
this.engineRunnable = engineRunnable;
//因为 EngineRunnable 是继承的 Runnable 所以执行 EngineRunnable 的 run 方法
//还要注意的是这个地方是 在 缓存服务中提交 了工作线程
future = diskCacheService.submit(engineRunnable);
}
我们接着看 EngineRunnable 的 run 方法 ,因为这个方法会走两次稍微注意一下
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
//开始获得数据
resource = decode();
} catch (OutOfMemoryError e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Out Of Memory Error decoding", e);
}
exception = new ErrorWrappingGlideException(e);
} 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);
}
}
//这个方法将会调用两次
//第一次:因为在构造中 this.stage = Stage.CACHE 所以第一次肯定调用 decodeFromCache() 方法
//第二次:因为 stage = Stage.SOURCE 状态改变 所以调用 decodeFromSource()
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;
}
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
private void onLoadComplete(Resource resource) {
manager.onResourceReady(resource);
}
//如果资源加载失败会把stage 加载状态修改 然后调用 回调接口去请求资源
private void onLoadFailed(Exception e) {
if (isDecodingFromCache()) {
stage = Stage.SOURCE;
manager.submitForSource(this);
} else {
//如果资源请求还失败就会抛出异常
manager.onException(e);
}
}
如果加载资源的话 会调用 decodeJob.decodeFromSource()
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();
//它是通过加载器 fetcher 来获取资源 我们的环境是(加载 String 的 Url 并且没有配置第三方网络加载器)
//那么 fetcher 在哪里来的呢 还记不记得 GenericRequest 的 onSizeReady() 方法 ,他是从 modelLoader.getResourceFetcher() 获取的 那么 modelLoader 哪来的呢
//你可以去 Glide 构造里发现 register(String.class, InputStream.class, new StreamStringLoader.Factory()); 这么一句代码
//我们看到了 StreamStringLoader 这个类 但是并没有发现 getResourceFetcher() 方法,我们看一下他的父类 StringLoader 现在发现了 getResourceFetcher 方法 在看到父类的时候我们又发现 父类是一个带参的构造,StreamStringLoader 在构造的时候查找了 Uri 的加载器给了父类 (查找就在流程我就不细分析了,自己看源码吧)
//我们又在 Glide 构造里查到 Uri 的加载器是 StreamUriLoader
//进去有一看 StreamUriLoader 继承自构造 UriLoader 的时候 传入了 GlideUrl 类型的加载器
//接着找到 HttpUrlGlideUrlLoader ,我们接着看 UriLoader 的 getResourceFetcher() 方法 他判断了资源是本地资源还是网络资源,本地资源就直接加载,方法自己看一下吧,否则就调用 HttpUrlGlideUrlLoader 进行网络加载
// HttpUrlGlideUrlLoader 的 getResourceFetcher 是个 HttpUrlFetcher 调用 loadData 进行网络加载,怎么加载的代码自己看一下吧,我就不贴了
//这个地方比较乱,大家慢慢理一下
final A data = fetcher.loadData(priority);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Fetched data", startTime);
}
if (isCancelled) {
return null;
}
//如果打开磁盘缓存就将在的资源缓存起来,然后再拿出来 ,并且装换成 Resource
//如果没有开启磁盘缓存 就直接转换为 Resource
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
图片的变换下篇文章讲,如果没有什么妖蛾子的话该调用 onLoadComplete() 方法,回调 EngineJob.onResourceReady(resource); 的方法 ,接着往下跟踪发现来到了如下方法
private void handleResultOnMainThread() {
//如果任务被停止则清除数据
if (isCancelled) {
resource.recycle();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
}
//包装资源
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
//记录是否回调的标志 此时 acquire 为 1
engineResource.acquire();
//回调 Engine 的 onEngineJobComplete 的方法做了这么几件事件事
//1. 添加 Engine 的回调
//2. 缓存资源
//3. 将任务在 jobs 中删除任务
listener.onEngineJobComplete(key, engineResource);
//调用所有等待此资源加载的回调
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
//回调一次 acquire 的数值加 1
engineResource.acquire();
//回调 GenericRequest 的 onResourceReady 代码看下方
cb.onResourceReady(engineResource);
}
}
//查看acquire 的值减 1 是否等于0,如果等于零,就说明此资源没有任何回调,则
回调Engine 的 onResourceReleased 在活动资源缓存中删除,并且判断是否缓存到内存中,然后清理释放资源
engineResource.release();
}
接下来看 GenericRequest 的 onResourceReady() 方法
资源有了剩下的就是将它放到 imageView 上
public void onResourceReady(Resource<?> resource) {
...
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
//如果资源为 NULL 则清除掉活动资源,并缓存
releaseResource(resource);
...
return;
}
if (!canSetResource()) {
releaseResource(resource);
// We can't set the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady(resource, (R) received);
}
继续查看 onResourceReady 方法
private void onResourceReady(Resource<?> resource, R result) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
//往上查 requestListener 这个参数,发现是在 GenericRequestBuilder 中的 buildRequestRecursive 方法中,咱们的情景设置是没有设置缩放,所以 requestListener 是 null
的
//requestListener 可以在外面设置Glide 加载失败或成功的监听
if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
isFirstResource)) {
//加载动画
GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
//资源设置
target.onResourceReady(result, animation);
}
notifyLoadSuccess();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: "
+ (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache);
}
如果资源加载失败则回调 GenericRequest 的 onException 方法
status = Status.FAILED; //修改状态
//TODO: what if this is a thumbnail request?
//requestListener 可以在外面设置Glide 加载失败或成功的监听
if (requestListener == null || !requestListener.onException(e, model, target, isFirstReadyResource())) {
setErrorPlaceholder(e); //给view设置错误占位符
}
到此资源的获取加载流程就完了
小结
到此我们已经简单的分析了一遍图片资源在网络上加载,并且设置到 view 中,欢迎大家品尝,并提出意见
注:
本篇基于 Glide 3.8.0
没有评论