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线程从硬盘缓存中读取。(为什么要先放进去,再读取呢?还是我理解错了)

原创粉丝点击