Android圖片加載框架Gilde源碼層深入分析

1.使用Gilde顯示一張圖片

Glide.with(this).
        load("https://cn.bing.com/sa/simg/hpb/xxx.jpg")
        .into(imageView);

上邊是Glide最簡單的來顯示一張圖片,雖然隻有三步操作:with、load、into,但是gilde卻通過大量的代碼在維護著。

with:返回一個RequestManager

load:返回一個RequestBuilder

下面通過源碼分析這三塊,重點是Glide生明周期的管理和緩存的使用,以及整個加載的流程的梳理。

2.Glide with操作源碼解析

Glide的with函數由於參數類型原因有多個重載,下面選擇傳入Activity當做參數的

public static RequestManager with(@NonNull Activity activity) {
  return getRetriever(activity).get(activity);
}

這個方法就是得到一個RequestManager

分析getRetriever

private static RequestManagerRetriever getRetriever(@Nullable Context context) {
  Preconditions.checkNotNull(
      context,
      "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
          + "returns null (which usually occurs when getActivity() is called before the Fragment "
          + "is attached or after the Fragment is destroyed).");
  return Glide.get(context).getRequestManagerRetriever();
}

得到一個RequestManagerRetriever,這個管理RequestManager

RequestManagerRetriever的get方法

public RequestManager get(@NonNull Context context) {
  if (context == null) {
    throw new IllegalArgumentException("You cannot start a load on a null Context");
  } else if (Util.isOnMainThread() && !(context instanceof Application)) {
    if (context instanceof FragmentActivity) {
      return get((FragmentActivity) context);
    } else if (context instanceof Activity) {
      return get((Activity) context);
    } else if (context instanceof ContextWrapper
        // Only unwrap a ContextWrapper if the baseContext has a non-null application context.
        // Context#createPackageContext may return a Context without an Application instance,
        // in which case a ContextWrapper may be used to attach one.
        && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
      return get(((ContextWrapper) context).getBaseContext());
    }
  }
  return getApplicationManager(context);
}

根據不同的參數類型,執行不同的代碼邏輯。

參數是:FragmentActivity的源碼

public RequestManager get(@NonNull FragmentActivity activity) {
  if (Util.isOnBackgroundThread()) {
    return get(activity.getApplicationContext());
  } else {
    assertNotDestroyed(activity);
    frameWaiter.registerSelf(activity);
    FragmentManager fm = activity.getSupportFragmentManager();
    return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
  }
}

註意這裡判斷瞭線程,如果是子線程會直接走ApplicationContext類型的函數重載。

supportFragmentGet方法:

private RequestManager supportFragmentGet(
    @NonNull Context context,
    @NonNull FragmentManager fm,
    @Nullable Fragment parentHint,
    boolean isParentVisible) {
  SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
  RequestManager requestManager = current.getRequestManager();
  if (requestManager == null) {
    // TODO(b/27524013): Factor out this Glide.get() call.
    Glide glide = Glide.get(context);
    requestManager =
        factory.build(
            glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
    // This is a bit of hack, we're going to start the RequestManager, but not the
    // corresponding Lifecycle. It's safe to start the RequestManager, but starting the
    // Lifecycle might trigger memory leaks. See b/154405040
    if (isParentVisible) {
      requestManager.onStart();
    }
    current.setRequestManager(requestManager);
  }
  return requestManager;
}

這個方法會創建一個空白的SupportRequestManagerFragment,這個Fragment的作用很重要,也是Glide的核心,聲明周期的管理就是靠這個來監控的。

監控瞭:onStart、onStop、onDestroy,所以Glide在使用的時候,我們不用關心資源的釋放,因為Glide會自動釋放資源,釋放資源的依據就是這個聲明周期的管理。

get函數參數是:ApplicationContext 的源碼

private RequestManager getApplicationManager(@NonNull Context context) {
  // Either an application context or we're on a background thread.
  if (applicationManager == null) {
    synchronized (this) {
      if (applicationManager == null) {
        Glide glide = Glide.get(context.getApplicationContext());
        applicationManager =
            factory.build(
                glide,
                new ApplicationLifecycle(),
                new EmptyRequestManagerTreeNode(),
                context.getApplicationContext());
      }
    }
  }
  return applicationManager;
}

可見當參數類型是:ApplicationContext或者子線程調用with方法不會創建空白的Fragmnet來進行生命周期的監控,也不會進行資源釋放,所以在調用with方法的時候不能傳ApplicationContext。

下面分析Glide分別在onStart、onStop、onDestroy執行瞭那些邏輯

RequestManager的onStart方法

public synchronized void onStart() {
  resumeRequests();
  targetTracker.onStart();
}

執行瞭刷新請求和Tracker的onStart,resumeRequests這個跟請求的緩存的使用

RequestTracker 的resumeRequests方法

public void resumeRequests() {
  isPaused = false;
  for (Request request : Util.getSnapshot(requests)) {
    if (!request.isComplete() && !request.isRunning()) {
      request.begin();
    }
  }
  pendingRequests.clear();
}

將運行隊列requests所有沒有完成又處於停止狀態的任務全部開始執行,requests是一個正在運行的請求緩存集合,這是一個set集合,處於等待狀態的請求隊列pendingRequests進行清理操作。這個就是Glide感應到聲明周期onStart進行的操作。

RequestManager的onStop方法

public synchronized void onStop() {
  pauseRequests();
  targetTracker.onStop();
}

RequestTracker的pauseRequests方法

public void pauseRequests() {
  isPaused = true;
  for (Request request : Util.getSnapshot(requests)) {
    if (request.isRunning()) {
      request.pause();
      pendingRequests.add(request);
    }
  }
}

request.pause():將所有的運行緩存集合中的請求進行暫停操作,並將這些請求加入到等待緩存請求集合pendingRequests

RequestManager的onDestroy

public synchronized void onDestroy() {
  targetTracker.onDestroy();
  for (Target<?> target : targetTracker.getAll()) {
    clear(target);
  }
  targetTracker.clear();
  requestTracker.clearRequests();
  lifecycle.removeListener(this);
  lifecycle.removeListener(connectivityMonitor);
  Util.removeCallbacksOnUiThread(addSelfToLifecycle);
  glide.unregisterRequestManager(this);
}

可以看到Glide在感應到onDestroy會進行各種資源釋放,監聽的移除操作。

3.Glide 的load操作

RequestManager的load方法

public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
  return asDrawable().load(bitmap);
}
public RequestBuilder<Drawable> asDrawable() {
   return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
    @NonNull Class<ResourceType> resourceClass) {
   return new RequestBuilder<>(glide, this, resourceClass, context);
}

load方法返回值是RequestBuilder,通過new的方式進行創建

RequestBuilder的構造方法

protected RequestBuilder(
    @NonNull Glide glide,
    RequestManager requestManager,
    Class<TranscodeType> transcodeClass,
    Context context) {
  this.glide = glide;
  this.requestManager = requestManager;
  this.transcodeClass = transcodeClass;
  this.context = context;
  this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
  this.glideContext = glide.getGlideContext();
  initRequestListeners(requestManager.getDefaultRequestListeners());
  apply(requestManager.getDefaultRequestOptions());
}

initRequestListeners:初始化監聽

apply:校驗參數

load的過程比較簡單,重點的into將會非常復雜

4.Glide的into流程解析

public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
  return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
}
<Y extends Target<TranscodeType>> Y into(
    @NonNull Y target,
    @Nullable RequestListener<TranscodeType> targetListener,
    Executor callbackExecutor) {
    return into(target, targetListener, /*options=*/ this, callbackExecutor);
 }

into另外一個重載方法:

private <Y extends Target<TranscodeType>> Y into(
    @NonNull Y target,
    @Nullable RequestListener<TranscodeType> targetListener,
    BaseRequestOptions<?> options,
    Executor callbackExecutor) {
  Preconditions.checkNotNull(target);
  if (!isModelSet) {
    throw new IllegalArgumentException("You must call #load() before calling #into()");
  }
  Request request = buildRequest(target, targetListener, options, callbackExecutor);
  Request previous = target.getRequest();
  if (request.isEquivalentTo(previous)
      && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
    if (!Preconditions.checkNotNull(previous).isRunning()) {
      previous.begin();
    }
    return target;
  }
  requestManager.clear(target);
  target.setRequest(request);
  requestManager.track(target, request);
  return target;
}

buildRequest 會通過SingleRequest.obtain,創建一個SingleRequest,這個必須要前置瞭解,後邊的流程需要用到。

requestManager.track(target, request):這個是主流程,下面來看RequestManager的track方法

RequestManager的track方法

synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
  targetTracker.track(target);
  requestTracker.runRequest(request);
}

RequestTracker的runRequest方法

public void runRequest(@NonNull Request request) {
  requests.add(request);
  if (!isPaused) {
    request.begin();
  } else {
    request.clear();
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      Log.v(TAG, "Paused, delaying request");
    }
    pendingRequests.add(request);
  }
}

這裡的Request是一個接口,調用瞭begin方法,這個Request就是前面創建的SingleRequest

SingleRequest的begin

public void begin() {
  synchronized (requestLock) {
    ...
    if (status == Status.COMPLETE) {
      onResourceReady(
          resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
      return;
    }
    cookie = GlideTrace.beginSectionAsync(TAG);
    status = Status.WAITING_FOR_SIZE;
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      onSizeReady(overrideWidth, overrideHeight);
    } else {
      target.getSize(this);
    }
    if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
        && canNotifyStatusChanged()) {
      target.onLoadStarted(getPlaceholderDrawable());
    }
    if (IS_VERBOSE_LOGGABLE) {
      logV("finished run method in " + LogTime.getElapsedMillis(startTime));
    }
  }
}

如果沒有設置寬高會調用 target.getSize(this)來獲取大小,如果設置瞭會執行onSizeReady方法

SingleRequest的onSizeReady方法

public void onSizeReady(int width, int height) {
  stateVerifier.throwIfRecycled();
  synchronized (requestLock) {
    if (IS_VERBOSE_LOGGABLE) {
      logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
    if (status != Status.WAITING_FOR_SIZE) {
      return;
    }
    status = Status.RUNNING;
    float sizeMultiplier = requestOptions.getSizeMultiplier();
    this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
    this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

    if (IS_VERBOSE_LOGGABLE) {
      logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
    }
    loadStatus =
        engine.load(
            glideContext,
            model,
            requestOptions.getSignature(),
            this.width,
            this.height,
            requestOptions.getResourceClass(),
            transcodeClass,
            priority,
            requestOptions.getDiskCacheStrategy(),
            requestOptions.getTransformations(),
            requestOptions.isTransformationRequired(),
            requestOptions.isScaleOnlyOrNoTransform(),
            requestOptions.getOptions(),
            requestOptions.isMemoryCacheable(),
            requestOptions.getUseUnlimitedSourceGeneratorsPool(),
            requestOptions.getUseAnimationPool(),
            requestOptions.getOnlyRetrieveFromCache(),
            this,
            callbackExecutor);
    if (status != Status.RUNNING) {
      loadStatus = null;
    }
    if (IS_VERBOSE_LOGGABLE) {
      logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
  }
}

接下來調用Engin的load方法

Engin的load的方法

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,
    boolean isScaleOnlyOrNoTransform,
    Options options,
    boolean isMemoryCacheable,
    boolean useUnlimitedSourceExecutorPool,
    boolean useAnimationPool,
    boolean onlyRetrieveFromCache,
    ResourceCallback cb,
    Executor callbackExecutor) {
  long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
  EngineKey key =
      keyFactory.buildKey(
          model,
          signature,
          width,
          height,
          transformations,
          resourceClass,
          transcodeClass,
          options);
  EngineResource<?> memoryResource;
  synchronized (this) {
    memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
    if (memoryResource == null) {
      return waitForExistingOrStartNewJob(
          glideContext,
          model,
          signature,
          width,
          height,
          resourceClass,
          transcodeClass,
          priority,
          diskCacheStrategy,
          transformations,
          isTransformationRequired,
          isScaleOnlyOrNoTransform,
          options,
          isMemoryCacheable,
          useUnlimitedSourceExecutorPool,
          useAnimationPool,
          onlyRetrieveFromCache,
          cb,
          callbackExecutor,
          key,
          startTime);
    }
  }
  // Avoid calling back while holding the engine lock, doing so makes it easier for callers to
  // deadlock.
  cb.onResourceReady(
      memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
  return null;
}

loadFromMemory這個方法是先從緩存中去查找,如果有直接就可以直接使用瞭

loadFromMemory方法

@Nullable
private EngineResource<?> loadFromMemory(
    EngineKey key, boolean isMemoryCacheable, long startTime) {
  if (!isMemoryCacheable) {
    return null;
  }
  EngineResource<?> active = loadFromActiveResources(key);
  if (active != null) {
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Loaded resource from active resources", startTime, key);
    }
    return active;
  }
  EngineResource<?> cached = loadFromCache(key);
  if (cached != null) {
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Loaded resource from cache", startTime, key);
    }
    return cached;
  }
  return null;
}

loadFromActiveResources:從活動緩存查找,如果有直接返回使用,這個緩存是一個Map集合

loadFromCache:從內存緩存查找,如果有直接使用,這個是一個LRU集合

上邊兩個是都是內存緩存,隻要顯示在頁面上的,都會緩存在活動緩存。

Engin的waitForExistingOrStartNewJob方法

private <R> LoadStatus waitForExistingOrStartNewJob(
    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,
    boolean isScaleOnlyOrNoTransform,
    Options options,
    boolean isMemoryCacheable,
    boolean useUnlimitedSourceExecutorPool,
    boolean useAnimationPool,
    boolean onlyRetrieveFromCache,
    ResourceCallback cb,
    Executor callbackExecutor,
    EngineKey key,
    long startTime) {
  EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
  if (current != null) {
    current.addCallback(cb, callbackExecutor);
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Added to existing load", startTime, key);
    }
    return new LoadStatus(cb, current);
  }
  EngineJob<R> engineJob =  //這個是線程池的管理者這個裡邊存著各個管理
      engineJobFactory.build(
          key,
          isMemoryCacheable,
          useUnlimitedSourceExecutorPool,
          useAnimationPool,
          onlyRetrieveFromCache);
  DecodeJob<R> decodeJob =  //這個是具體的任務
      decodeJobFactory.build(
          glideContext,
          model,
          key,
          signature,
          width,
          height,
          resourceClass,
          transcodeClass,
          priority,
          diskCacheStrategy,
          transformations,
          isTransformationRequired,
          isScaleOnlyOrNoTransform,
          onlyRetrieveFromCache,
          options,
          engineJob);
  jobs.put(key, engineJob);
  engineJob.addCallback(cb, callbackExecutor);
  engineJob.start(decodeJob);
  if (VERBOSE_IS_LOGGABLE) {
    logWithTimeAndKey("Started new load", startTime, key);
  }
  return new LoadStatus(cb, engineJob);
}

engineJob 管理一個線程池,decodeJob這個是任務的抽象是一個Runable,所以下面就會執行任務的run方法

DecodeJob的run方法

public void run() {
  ...
  try {
    if (isCancelled) {
      notifyFailed();
      return;
    }
    runWrapped();
  } catch (CallbackException e) {
    // If a callback not controlled by Glide throws an exception, we should avoid the Glide
    // specific debug logic below.
    throw e;
  } catch (Throwable t) {
    ..
  } finally {
    // Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call
    // close in all cases anyway.
    if (localFetcher != null) {
      localFetcher.cleanup();
    }
    GlideTrace.endSection();
  }
}

DecodeJob的runWrapped方法

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);
  }
}

默認會執行case INITIALIZE的runGenerators(),getNextGenerator得到的是SourceGenerator,這個必須要明確,否則走不下去。

SourceGenerator的startNext方法

public boolean startNext() {
  ...
  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;
      startNextLoad(loadData);
    }
  }
  return started;
}

DecodeHelper的getLoadData方法

List<LoadData<?>> getLoadData() {
  if (!isLoadDataSet) {
    isLoadDataSet = true;
    loadData.clear();
    List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = modelLoaders.size(); i < size; i++) {
      ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
      LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
      if (current != null) {
        loadData.add(current);
      }
    }
  }
  return loadData;
}

HttpGlideUrlLoader的buildLoadData方法

public LoadData<InputStream> buildLoadData(
    @NonNull GlideUrl model, int width, int height, @NonNull Options options) {
  // GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
  // spent parsing urls.
  GlideUrl url = model;
  if (modelCache != null) {
    url = modelCache.get(model, 0, 0);
    if (url == null) {
      modelCache.put(model, 0, 0, model);
      url = model;
    }
  }
  int timeout = options.get(TIMEOUT);
  return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}

可以看到最終返回的是包含HttpUrlFetcher的loadData

SourceGenerator的startNextLoad方法

private void startNextLoad(final LoadData<?> toStart) {
  loadData.fetcher.loadData(
      helper.getPriority(),
      new DataCallback<Object>() {
        @Override
        public void onDataReady(@Nullable Object data) {
          if (isCurrentRequest(toStart)) {
            onDataReadyInternal(toStart, data);
          }
        }
        @Override
        public void onLoadFailed(@NonNull Exception e) {
          if (isCurrentRequest(toStart)) {
            onLoadFailedInternal(toStart, e);
          }
        }
      });
}

loadData.fetcher的fetcher就是HttpUrlFetcher

HttpUrlFetcher的loadData方法

public void loadData(
    @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
  long startTime = LogTime.getLogTime();
  try {
    InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
    callback.onDataReady(result);
  } catch (IOException e) {
    if (Log.isLoggable(TAG, Log.DEBUG)) {
      Log.d(TAG, "Failed to load data for url", e);
    }
    callback.onLoadFailed(e);
  } finally {
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
    }
  }
}

loadDataWithRedirects方法

private InputStream loadDataWithRedirects(
    URL url, int redirects, URL lastUrl, Map<String, String> headers) throws HttpException {
  if (redirects >= MAXIMUM_REDIRECTS) {
    throw new HttpException(
        "Too many (> " + MAXIMUM_REDIRECTS + ") redirects!", INVALID_STATUS_CODE);
  } 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", INVALID_STATUS_CODE);
      }
    } catch (URISyntaxException e) {
      // Do nothing, this is best effort.
    }
  }
  urlConnection = buildAndConfigureConnection(url, headers);
  try {
    // Connect explicitly to avoid errors in decoders if connection fails.
    urlConnection.connect();
    // Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
    stream = urlConnection.getInputStream();
  } catch (IOException e) {
    throw new HttpException(
        "Failed to connect or obtain data", getHttpStatusCodeOrInvalid(urlConnection), e);
  }
  ...
}

到這裡就看到urlConnection拿到圖片的InputStream,終於從一個網絡鏈接請求到流瞭,然後就是將流轉成bitmap顯示的過程瞭,感興趣的可以繼續查看。

5.Glide

聲明周期和緩存總結 Glide 調用with函數如果傳入的ApplicationContext或者在子線程不會為我們創建空白的Fragment,不會進行生命周期的監控,也就不會進行資源自動釋放,所以不用傳ApplicationContext。其他情況glide會自動創建空白的Fragment來進行生命周期監控,並自動進行資源釋放。

Glide的緩存分為4級緩存:活動緩存、內存緩存、磁盤緩存、網絡緩存

  • 活動緩存,是一個Map集合,加載圖片會首先從這個緩存裡邊查找。
  • 內存緩存,是一個LRU緩存集合,如果獲取緩存沒有就從內存緩存查找。
  • 磁盤緩存,是disklrucache緩存集合,如果從內存緩存沒有找到就從磁盤緩存查找。
  • 網絡緩存,如果磁盤緩存沒有就需要從網絡上就行請求加載瞭。

到此這篇關於Android圖片加載框架Gilde源碼層深入分析的文章就介紹到這瞭,更多相關Android Gilde內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: