Glide 4.0源码分析(3)
来源:互联网 发布:外卖小票打印软件 编辑:程序博客网 时间:2024/06/05 17:55
说明:源码采用Glide4.0.0版本
Glide用法:
Glide.with(this).load(url) .into(imageView);
1、with()返回RequestManager对象
2、load()方法返回RequestBuilder对象
3、into()方法返回Target对象
RequestBuilder.java的into()方法如下
public Target<TranscodeType> into(ImageView view) { ...省略部分代码 return into(context.buildImageViewTarget(view, transcodeClass)); }
public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) { ... Request request = buildRequest(target); target.setRequest(request); requestManager.track(target, request); return target; }
RequestManager.java
void track(Target<?> target, Request request) { targetTracker.track(target); requestTracker.runRequest(request); }
RequestTracker.java
public void runRequest(Request request) { requests.add(request); if (!isPaused) { request.begin(); } else { pendingRequests.add(request); } }
SingleRequest.class(reqeust.begin()方法的实现类)
public void begin() { ... if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight);//重点 } else { target.getSize(this); } ... }
public void onSizeReady(int width, int 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(), requestOptions.getUseUnlimitedSourceGeneratorsPool(), requestOptions.getOnlyRetrieveFromCache(), this); ... }
Enjine.java
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, boolean useUnlimitedSourceExecutorPool, boolean onlyRetrieveFromCache, ResourceCallback cb) { Util.assertMainThread(); long startTime = LogTime.getLogTime(); EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); //步骤1:内存缓存,由LruCache实现 EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); if (cached != null) { cb.onResourceReady(cached, DataSource.MEMORY_CACHE); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from cache", startTime, key); } return null; } //步骤2:内存缓存,由HashMap弱引用实现 EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); if (active != null) { cb.onResourceReady(active, DataSource.MEMORY_CACHE); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from active resources", startTime, key); } return null; } //步骤3:这个应该也属于内存缓存,这里避免了EngineJob的重复创建,EngineJob代表从硬盘缓存或者网络上加载图片并进行decode的整个过程。 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); } //步骤4:构建EngineJob,从硬盘缓存或者网络上加载图片 EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable, useUnlimitedSourceExecutorPool); DecodeJob<R> decodeJob = decodeJobFactory.build( glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, onlyRetrieveFromCache, options, engineJob); jobs.put(key, engineJob); engineJob.addCallback(cb); engineJob.start(decodeJob);//重点 if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Started new load", startTime, key); } return new LoadStatus(cb, engineJob); }
EngineJob.java
利用线程池执行DecodeJob
public void start(DecodeJob<R> decodeJob) { this.decodeJob = decodeJob; GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor(); executor.execute(decodeJob); }
DecodeJob.java实现了Runable接口,下面是它的run()方法
@Override public void run() { // This should be much more fine grained, but since Java's thread pool implementation silently // swallows all otherwise fatal exceptions, this will at least make it obvious to developers // that something is failing. TraceCompat.beginSection("DecodeJob#run"); try { if (isCancelled) { notifyFailed(); return; } runWrapped();//重点 } catch (RuntimeException e) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage, e); } // When we're encoding we've already notified our callback and it isn't safe to do so again. if (stage != Stage.ENCODE) { notifyFailed(); } if (!isCancelled) { throw e; } } finally { if (currentFetcher != null) { currentFetcher.cleanup(); } TraceCompat.endSection(); } }
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); } }
private void runGenerators() { currentThread = Thread.currentThread(); startFetchTime = LogTime.getLogTime(); boolean isStarted = false; while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) {//重点 stage = getNextStage(stage); currentGenerator = getNextGenerator(); if (stage == Stage.SOURCE) { reschedule(); return; } } // We've run out of stages and generators, give up. if ((stage == Stage.FINISHED || isCancelled) && !isStarted) { notifyFailed(); } // Otherwise a generator started a new load and we expect to be called back in // onDataFetcherReady. }
SourceGenerator.java的startNext方法
@Override public boolean startNext() { if (dataToCache != null) { Object data = dataToCache; dataToCache = null; cacheData(data); } if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) { return true; } sourceCacheGenerator = null; loadData = null; boolean started = false; 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.fetcher.loadData(helper.getPriority(), this);//重点 } } return started; }
loadData.fetcher.loadData(helper.getPriority(), this)的实现是
HttpUrlFetcher.java
public void loadData(Priority priority, DataCallback<? super InputStream> callback) { long startTime = LogTime.getLogTime(); final InputStream result; try { //步骤1:通过网路下载图片,并以InputStream的形式返回 result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders()); } catch (IOException e) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Failed to load data for url", e); } callback.onLoadFailed(e); return; } if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime) + " ms and loaded " + result); } //步骤2:把网络下载的InputStream返回 callback.onDataReady(result); }
步骤1:执行过程如下
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 { // 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 HttpException("In re-direct loop"); } } catch (URISyntaxException e) { // Do nothing, this is best effort. } } urlConnection = connectionFactory.build(url); for (Map.Entry<String, String> headerEntry : headers.entrySet()) { urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue()); } urlConnection.setConnectTimeout(timeout); urlConnection.setReadTimeout(timeout); urlConnection.setUseCaches(false); urlConnection.setDoInput(true); // Stop the urlConnection instance of HttpUrlConnection from following redirects so that // redirects will be handled by recursive calls to this method, loadDataWithRedirects. urlConnection.setInstanceFollowRedirects(false); // 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 HttpException("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 HttpException(statusCode); } else { throw new HttpException(urlConnection.getResponseMessage(), statusCode); } }
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection) throws IOException { if (TextUtils.isEmpty(urlConnection.getContentEncoding())) { int contentLength = urlConnection.getContentLength(); stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength); } else { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding()); } stream = urlConnection.getInputStream(); } return stream; }
步骤2:执行过程如下
SourceGenerator.java 的onDataReady()方法
@Override public void onDataReady(Object data) { DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy(); if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) { dataToCache = data; // We might be being called back on someone else's thread. Before doing anything, we should // reschedule to get back onto Glide's thread. cb.reschedule(); } else { cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher, loadData.fetcher.getDataSource(), originalKey); } }
回调DecodeJob.java的onDataFetcherReady()方法
@Override public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) { this.currentSourceKey = sourceKey; this.currentData = data; this.currentFetcher = fetcher; this.currentDataSource = dataSource; this.currentAttemptingKey = attemptedKey; if (Thread.currentThread() != currentThread) { runReason = RunReason.DECODE_DATA; callback.reschedule(this); } else { TraceCompat.beginSection("DecodeJob.decodeFromRetrievedData"); try { decodeFromRetrievedData();//重点 } finally { TraceCompat.endSection(); } } }
private void decodeFromRetrievedData() { ... Resource<R> resource = null; try { //1:编码成Resource类型 resource = decodeFromData(currentFetcher, currentData, currentDataSource); } catch (GlideException e) { e.setLoggingDetails(currentAttemptingKey, currentDataSource); exceptions.add(e); } if (resource != null) { //2:将resource返回 notifyEncodeAndRelease(resource, currentDataSource); } else { runGenerators(); } }
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) { if (resource instanceof Initializable) { ((Initializable) resource).initialize(); } Resource<R> result = resource; LockedResource<R> lockedResource = null; if (deferredEncodeManager.hasResourceToEncode()) { lockedResource = LockedResource.obtain(resource); result = lockedResource; } notifyComplete(result, dataSource); stage = Stage.ENCODE; try { if (deferredEncodeManager.hasResourceToEncode()) { deferredEncodeManager.encode(diskCacheProvider, options); } } finally { if (lockedResource != null) { lockedResource.unlock(); } onEncodeComplete(); } }
private void notifyComplete(Resource<R> resource, DataSource dataSource) { setNotifiedOrThrow(); callback.onResourceReady(resource, dataSource); }
EngineJob.java的onResourceReady()方法
@Override public void onResourceReady(Resource<R> resource, DataSource dataSource) { this.resource = resource; this.dataSource = dataSource; //发送一条Message MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget(); }
下面是这条消息的处理
private static class MainThreadCallback implements Handler.Callback { @Synthetic MainThreadCallback() { } @Override public boolean handleMessage(Message message) { EngineJob<?> job = (EngineJob<?>) message.obj; switch (message.what) { case MSG_COMPLETE: job.handleResultOnMainThread();//重点 break; ... } return true; } }
void handleResultOnMainThread() { stateVerifier.throwIfRecycled(); if (isCancelled) { resource.recycle(); release(false /*isRemovedFromQueue*/); return; } else if (cbs.isEmpty()) { throw new IllegalStateException("Received a resource without any callbacks to notify"); } else if (hasResource) { throw new IllegalStateException("Already have resource"); } engineResource = engineResourceFactory.build(resource, isCacheable); hasResource = true; // Hold on to resource for duration of request so we don't recycle it in the middle of // notifying if it synchronously released by one of the callbacks. engineResource.acquire(); listener.onEngineJobComplete(key, engineResource); for (ResourceCallback cb : cbs) { if (!isInIgnoredCallbacks(cb)) { engineResource.acquire(); cb.onResourceReady(engineResource, dataSource);//重点 } } // Our request is complete, so we can release the resource. engineResource.release(); release(false /*isRemovedFromQueue*/); }
最后会回调到SingleRequest.java的private void onResourceReady(Resource resource, R result, DataSource dataSource)方法
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) { // We must call isFirstReadyResource before setting status. boolean isFirstResource = isFirstReadyResource(); status = Status.COMPLETE; this.resource = resource; if (glideContext.getLogLevel() <= Log.DEBUG) { Log.d(GLIDE_TAG, "Finished loading " + result.getClass().getSimpleName() + " from " + dataSource + " for " + model + " with size [" + width + "x" + height + "] in " + LogTime.getElapsedMillis(startTime) + " ms"); } if (requestListener == null || !requestListener.onResourceReady(result, model, target, dataSource, isFirstResource)) { Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource); target.onResourceReady(result, animation);//重点 } notifyLoadSuccess(); }
回调ImageViewTarget.java的onResourceReady()方法
@Override public void onResourceReady(Z resource, @Nullable Transition<? super Z> transition) { if (transition == null || !transition.transition(resource, this)) { setResourceInternal(resource); } else { maybeUpdateAnimatable(resource); } }
private void setResourceInternal(@Nullable Z resource) { maybeUpdateAnimatable(resource); setResource(resource); }
setResource(@Nullable Z resource)是一个抽象方法
protected abstract void setResource(@Nullable Z resource);
通过dubug发现最后调用了DrawableImageViewTarget.java的实现
@Override protected void setResource(@Nullable Drawable resource) { view.setImageDrawable(resource); }
以上。
在Glide4.0通过网络加载完图片的流程(和Glide3.7版本流程不不太一样):
1、先从通过GlideExecutor.newDiskCacheExecutor()(默认的硬盘缓存线程池,里面只有一个线程)新建的线程池中获取以前线程去硬盘中获取缓存,如果获取不到则执行2
2、从通过GlideExecutor.newSourceExecutor()新建的线程池中获取一个线程a,从网络上加载数据,然后启动线程b(b来自SourceExecutor,并且a停止),把a中加载的数据写入硬盘,然后在通过b线程从硬盘缓存中读取。(为什么要先放进去,再读取呢?还是我理解错了)
- Glide 4.0源码分析(3)
- Glide 4.0源码分析(1)
- Glide 4.0源码分析(2)
- Glide源码分析3 -- 绑定Activity生命周期
- glide源码分析
- Glide 源码分析
- Glide源码分析 一
- Glide源码分析
- Glide源码分析
- Glide源码分析1 - 框架
- Glide源码分析-生命周期管理
- Glide源码分析-下载图片
- Glide的源码分析<一>
- android开发-Glide源码分析
- Glide源码分析(二)——磁盘缓存实现
- Glide源码分析(四)——Registry机制
- Glide源码分析(五)——ModelLoader机制
- 深入理解Glide源码,分析之路(二):Glide的执行流程,史上最详细、易懂
- 二分查找,顺序查找
- caffe训练时loss=nan的原因
- C#使用Object类实现栈的方法详解
- Lines_gauss——检测图像中的线条及其宽度,在视觉表面检测方面应用广泛。
- java并发编程的一些总结
- Glide 4.0源码分析(3)
- php一些常见的错误以及处理方式
- 存储过程和函数具体的区别
- 2017.8.31开学第一周周中训练总结
- test
- 轮播图*3
- dns-prefetch性能优化
- 前端性能优化之图片优化方法
- DIV在100%下iframe宽度高度的自适应的问题