Glide源码分析——Glide基本用法、缓存

我们最常用的gilde加载图片的时候就用短短的方法就将图片展示出来了:

Glide.with(this).load(url).into(imageview);

在这简单的操作后面是很多的代码,先从这一个个方法开始看起。

1.with()

RequestManager with()

public class Glide{
    ·····
    //适用于在正常fragment之外使用的资源或activity生命周期(在服务中或者通知缩略图)
    //@param context 任何上下文都不会被保留
    //@return 可用于启动加载的顶级应用程序的RequestManager
    public static RequestManager with(@NonNull Context context) {
        return getRetriever(context).get(context);
  }
  
  //该加载与传入的activity生命周期相关联
  //
  public static RequestManager with(@NonNull Activity activity) {
        return getRetriever(activity).get(activity);
  }
  
  public static RequestManager with(FragmentActivity activity) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(activity);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public static RequestManager with(android.app.Fragment fragment) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(fragment);
    }

    public static RequestManager with(Fragment fragment) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(fragment);
    }
}
  • with()方法重载的中类很多,可以传入context、activity、fragment等
  • RequsetManager调用get方法获得RequestManagerRetriever对象
  • RequestManagerRetriever调用get方法获取RequestManager对象

Glide#getRetriever

@NonNull
  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();
}

调用Glide.get方法,初始化glide

@NonNull
public static Glide get(@NonNull Context context) {
    if (glide == null) {
      GeneratedAppGlideModule annotationGeneratedModule =
          getAnnotationGeneratedGlideModules(context.getApplicationContext());
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context, annotationGeneratedModule);
        }
      }
    }
    return glide;
}

Glide加载是个静态方法,且采用单例模式

RequestManagerRetriever#get()

public class RequestManagerRetriever implements Handler.Callback {
    ```
    private volatile RequestManager applicationManager;
    
    @NonNull
  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) {
          // Normally pause/resume is taken care of by the fragment we add to the fragment or
          // activity. However, in this case since the manager attached to the application will not
          // receive lifecycle events, we must force the manager to start resumed using
          // ApplicationLifecycle.

          // TODO(b/27524013): Factor out this Glide.get() call.
          Glide glide = Glide.get(context.getApplicationContext());
          applicationManager =
              factory.build(
                  glide,
                  new ApplicationLifecycle(),
                  new EmptyRequestManagerTreeNode(),
                  context.getApplicationContext());
        }
      }
    }

    return applicationManager;
  }
  
   @NonNull
  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);
  }

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

  @NonNull
  public RequestManager get(@NonNull Fragment fragment) {
    Preconditions.checkNotNull(
        fragment.getContext(),
        "You cannot start a load on a fragment before it is attached or after it is destroyed");
    if (Util.isOnBackgroundThread()) {
      return get(fragment.getContext().getApplicationContext());
    } else {
      FragmentManager fm = fragment.getChildFragmentManager();
      return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
    }
  }

  @SuppressWarnings("deprecation")
  @NonNull
  public RequestManager get(@NonNull Activity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else {
      assertNotDestroyed(activity);
      android.app.FragmentManager fm = activity.getFragmentManager();
      return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }
  ······
}

这里的get方法是根据传入的不同的参数制定的。实际上只分为两种参数Application类型的参数和传入非Application类型的参数。

  • 传入Application参数类型的情况:

在Glide.with()方法中传入的是一个Application对象,就会调用get()方法中参数为context的方法。然后会调用getApplicationManager()方法来获取一个RequestManager对象。

这是最简单的一种情况。因为Application对象的生命周期就是应用程序的生命周期。Glide的生命周期和应用程序的生命周期就是同步的。关闭应用程序,Glide的加载也会终止。

  • 传入非Application参数类型的情况:

这种情况下会像当前传入的Activity等中添加一个隐藏的fragment。比如说Glide正在加载一张图片如果此时关闭了当前应用,那么glide应当停止对当前图片的加载,但是glide并不知道当前activity的生命周期,但是fragment的生命周期和activity是同步的,若activity被销毁了,fragment是可以监听到的,因此添加一个隐藏的fragment。

RequestManagerRetriever#fragmentGet

添加隐藏的fragment在supportFragmentGet()和fragmentGet()方法中。这两个方法中有一个类RequestManagerFragment继承于fragment,创建了fragment对象。

private RequestManager fragmentGet(
    @NonNull Context context,
    @NonNull android.app.FragmentManager fm,
    @Nullable android.app.Fragment parentHint,
    boolean isParentVisible) {
    //创建fragment对象
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
    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);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

RequestManagerFragment

public class RequestManagerFragment extends Fragment {
    ······
    public RequestManagerFragment() {
        this(new ActivityFragmentLifecycle());
    }
    ······
}

这里面定义了一个ActivityFragmentLifecycle类来监听fragment生命周期的变化

ActivityFragmentLifecycle

LifecycleListener监听fragment生命周期

class ActivityFragmentLifecycle implements Lifecycle {
     @Override
  public void addListener(@NonNull LifecycleListener listener) {
    lifecycleListeners.add(listener);

    if (isDestroyed) {
      listener.onDestroy();
    } else if (isStarted) {
      listener.onStart();
    } else {
      listener.onStop();
    }
  }

  @Override
  public void removeListener(@NonNull LifecycleListener listener) {
    lifecycleListeners.remove(listener);
  }

  void onStart() {
    isStarted = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onStart();
    }
  }

  void onStop() {
    isStarted = false;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onStop();
    }
  }

  void onDestroy() {
    isDestroyed = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onDestroy();
    }
  }
}

2. load()

RequestManager

因为with()方法返回的是RequestManager对象,所以调用load方法的类肯定是RequestManager。load方法可加载的类型有很多,这里只拿加载图片的url为例。

我看的这一版的gilde已经是3.10.0的了。网上一些博客中的DrawTypeRequest已经没有了。

public class RequestManager
    implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>>{
    ······
    public RequestBuilder<Drawable> asDrawable() {
        return as(Drawable.class);
    }
    
    public RequestBuilder<Drawable> load(@Nullable URL url) {
        return asDrawable().load(url);
    }
    
    //as方法中传入的是Drawable对象
    @NonNull
    @CheckResult
    public RequestBuilder<Drawable> asDrawable() {
        return as(Drawable.class);
    }
    
    //resourceClass要解码的资源
    //该方法返回一个新的请求构建器,用于加载给定的资源类,进入RequestBuilder
    @NonNull
    @CheckResult
    public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
        return new RequestBuilder<>(glide, this, resourceClass, context);
    }
    ······
}

看到加载图片的load方法都返回RequestBuilder<Drawable>对象,且load方法都先由asDrawable()调用。

上面的源码是从load()-->asDrawable()-->as()的过程,as方法中的resourceClass就是传进来的Drawable.class这里的drawable指代的是一种资源的类型。下面再看下RequestBuilder这个类。

RequestManager中的load方法主要就是通过实例化RequestBuilder,再调用RequestBuilder中的load方法。

RequestBuilder

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
    
    //构造方法,as中调用的方法就是这个
    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());
  }
  
  ······
    //load加载的资源类型有很多种,这里还是只放了url的
    public RequestBuilder<TranscodeType> load(@Nullable Uri uri) {
        return loadGeneric(uri);
    }
    
    @NonNull
    private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
        this.model = model;
        isModelSet = true;
        return this;
    }
}

在这个类可以看见一个自定义的泛型,TranscodeType转码类型,他代表可以传进来的url file bitmp等不同类型的资源。官方介绍是一个通用类,可以处理通用资源类型的设置选项和启动负载。

load方法又调用了loadGeneric方法,这里面设置了两个变量,mode和isModleSet。

into

从load.into的调用关系可以知道into方法在RequestBuilder类中

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

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

@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);

    BaseRequestOptions<?> requestOptions = this;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      // Clone in this method so that if we use this RequestBuilder to load into a View and then
      // into a different target, we don't retain the transformation applied based on the previous
      // View's scale type.
      switch (view.getScaleType()) {
        case CENTER_CROP:
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
        case FIT_XY:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }
    
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
}

into方法最终会返回一个target对象。通过glide.buildImageViewTarget(view,transcodeClass)创建一个target对象,Target对象是最终用来展示图片的。

buildImageViewTarget

@NonNull
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public <Z> ViewTarget<ImageView, Z> buildTarget(
      @NonNull ImageView view, @NonNull Class<Z> clazz) {
    if (Bitmap.class.equals(clazz)) {
      return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
    } else if (Drawable.class.isAssignableFrom(clazz)) {
      return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
    } else {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
}

创建target对象主要分两种:

  • 加载图片时调用了asBitmap()方法,会构建BitmapImageViewTarget对象
  • 其他情况,构建GlideDrawableImageviewTarget对象

GenericRequestBuilder#into(Target target)

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加载资源的请求类 Target继承于LifecyclerListener,通过getRequest检索到此目标当前的请求
    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;
    //3.7.0版本
    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;
}

这里会检查是不是在主线程,因为更新ui的操作必须在主线程。这里通过target对象获取request对象,然后清除之前的request对象,回收request对象,然后重新构建一个request对象,并给这个target对象设置request对象。

下面的分析拿glide3.7.0的版本来看。现在4.10.0版本和之前的版本差别太大了。

在这之后就开始加载图片。buildRequest()方法构建出来Request对象,buildRequest-->buildRequestRecursive()-->obtainRequest-->GenericRequest.obtain()-->REQUEST_POOL.poll()。调用了请求池的poll方法,获取request对象。如果这个对象为空就创建一个GenericRequest对象,然后用request对象的init方法进行初始化。

runRequest

public void runRequest(Request request) {
       requests.add(request);
       if (!isPaused) {
           //执行request
           request.begin();
       } else {
           //将request加入待执行队列
           pendingRequests.add(request);
       }
}

先判断Glide当前是不是处于暂停状态。
在执行图片的加载过程中有几个重要的类:

  • ModeLoader:获取原始数据的类
  • ResourceTranscoder:图片资源转换的类
  • Engine:负责启动负载并管理活动和缓存的资源类
  • DataLoadProvider:负责图片的编码和解码
  • DataFetcher:将流资源转换为glide实际加载图片需要的数据,比如byte[] file uri url等
  • EngineJob:开启线程,为异步加载图片做准备

缓存

glide缓存主要分为内存缓存和硬盘缓存。这里将从这两个方向来分析。

缓存key

缓存功能要实现的话肯定是要有进行缓存的key的。拿到这个key我们才能找到对应的图片。

public class Engine implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {

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

        ...
    }
    ...
}

这里调用fetcher.getId()方法获得了一个id字符串,这个字符串是加载图片的唯一标识。EngineKey就是保存key的类,一个key有10个标识可以区分。

内存缓存

默认情况下,Glide自动就是会开启内存缓存的。如果不想开启内存缓存,只需要设置skipMemoryCache(true)就可以了。

Glide.with(this)
    .load(url)
    .skipMemoryCache(true)
    .into(imageView);

Glide缓存实现了LruCache算法,还结合了一种弱引用的机制,共同完成了内存缓存的功能。

Glide获取缓存资源对象

loadGeneric()-->Glide.buildStreamModelLoader()-->buildModelLoader()-->Glide.get()-->createGlide()

public class GlideBuilder {
    ...

    Glide createGlide() {
        if (sourceService == null) {
            final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
            sourceService = new FifoPriorityThreadPoolExecutor(cores);
        }
        if (diskCacheService == null) {
            diskCacheService = new FifoPriorityThreadPoolExecutor(1);
        }
        MemorySizeCalculator calculator = new MemorySizeCalculator(context);
        if (bitmapPool == null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                int size = calculator.getBitmapPoolSize();
                bitmapPool = new LruBitmapPool(size);
            } else {
                bitmapPool = new BitmapPoolAdapter();
            }
        }
        if (memoryCache == null) {
            //Glide实现内存缓存使用的LruCache对象
            memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
        }
        if (diskCacheFactory == null) {
            diskCacheFactory = new InternalCacheDiskCacheFactory(context);
        }
        if (engine == null) {
            engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
        }
        if (decodeFormat == null) {
            decodeFormat = DecodeFormat.DEFAULT;
        }
        return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
    }
}

LruCache算法的实现主要的点就在于它用了LindedHashMap来保存数据,其中的一个特性accessOrder = true是基于访问顺序的,再加上对LindkedHashMap的数据操作上锁实现的缓存策略。调用get方法访问缓存对象时,就会调用LinkedHashMap的get()方法获取对应集合的元素,同时会更新该元素到队列。当调用put()方法时,就会在集合中添加元素,并调用trimToSize()判断缓存是否以满,如果满了就用LinkedHashMap的迭代器删除对首元素,也就是近期最少访问的元素。

Engine#load()

内存缓存的代码在这里实现

public class Engine implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {
    ...    

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

    ...
}

在这个方法里其实可以把加载图片的过程分为三步:

  • loadFromCache():从缓存中获取
  • loadFromActivityResources():获取缓存图片
  • engineJob.start(runnable):开启线程从网络加载图片

Glide加载过程中会调用loadFromCache()和loadFromActivityResources()这两个方法来获取内存缓存。一个使用的是LruCache算法,另一个使用的是弱引用。

loadFromCache/loadFromActivityResources

public class Engine implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {

    private final MemoryCache cache;
    private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
    ...

    private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
        if (!isMemoryCacheable) {
            return null;
        }
        EngineResource<?> cached = getEngineResourceFromCache(key);
        if (cached != null) {
            cached.acquire();
            activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
        }
        return cached;
    }

    private EngineResource<?> getEngineResourceFromCache(Key key) {
        Resource<?> cached = cache.remove(key);
        final EngineResource result;
        if (cached == null) {
            result = null;
        } else if (cached instanceof EngineResource) {
            result = (EngineResource) cached;
        } else {
            result = new EngineResource(cached, true /*isCacheable*/);
        }
        return result;
    }

    private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
        if (!isMemoryCacheable) {
            return null;
        }
        EngineResource<?> active = null;
        WeakReference<EngineResource<?>> activeRef = activeResources.get(key);
        if (activeRef != null) {
            active = activeRef.get();
            if (active != null) {
                active.acquire();
            } else {
                activeResources.remove(key);
            }
        }
        return active;
    }

    ...
}

loadFromCache()方法先会判断是否开启了内存缓存,开启了则会调用getEngineResourceFromCache()方法从cache对象也就是LruResourceCache中获取图片资源,但是却把这个资源从缓存中removele。获取到这个资源后把这个资源又加入到了activeResources中。这很令我困惑,为什么这样做,按理来说Lru会根据获取到的这个资源会把它重新加入到队列后面,为了就是会被删除。

activeResources就是一个弱引用的HashMap,用来缓存正在使用中的图片。loadFromActiveResources()方法就是从activeResources这个HashMap当中取值的。使用activeResources来缓存正在使用中的图片可以保护这些图片不会被LruCache算法回收掉。

内存缓存的写入

当图片加载完成后,会在EngineJob当中通过Handler发送一条消息将执行逻辑切回到主线程当中,从而执行了handleResultOnMainThread()方法。

class EngineJob implements EngineRunnable.EngineRunnableManager {

    private final EngineResourceFactory engineResourceFactory;
    ...

    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;
        engineResource.acquire();
        listener.onEngineJobComplete(key, engineResource);
        for (ResourceCallback cb : cbs) {
            if (!isInIgnoredCallbacks(cb)) {
                engineResource.acquire();
                cb.onResourceReady(engineResource);
            }
        }
        engineResource.release();
    }

    static class EngineResourceFactory {
        public <R> EngineResource<R> build(Resource<R> resource, boolean isMemoryCacheable) {
            return new EngineResource<R>(resource, isMemoryCacheable);
        }
    }
    ...
}

EngineJob这个类就是主要就是来执行添加(addCallBack)和删除(rmoveCallBack)回调,并在回调时通知回调来管理负载的类加载完成。

handleResultOnMainThread()方法中通过EngineResourceFactory构建出了一个包含图片资源的EngineResource对象,将这个对象传入Engine的onEngineJobComplete方法中。

onEngineJobComplete

@Override
public void onEngineJobComplete(Key key, EngineResource<?> resource) {
    Util.assertMainThread();
    // A null resource indicates that the load failed, usually due to an exception.
    if (resource != null) {
        resource.setResourceListener(key, this);

        if (resource.isCacheable()) {
            activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
        }
    }
    // TODO: should this check that the engine job is still current?
    jobs.remove(key);
}

可看到回调过来的EngineResource被put到了activeResources当中,在这里将图片写入了缓存。但这里只是弱引用缓存,还有一种LruCache在哪里呢?

acquire()和release()

class EngineResource<Z> implements Resource<Z> {

    private int acquired;
    ...

    void acquire() {
        if (isRecycled) {
            throw new IllegalStateException("Cannot acquire a recycled resource");
        }
        if (!Looper.getMainLooper().equals(Looper.myLooper())) {
            throw new IllegalThreadStateException("Must call acquire on the main thread");
        }
        ++acquired;
    }

    void release() {
        if (acquired <= 0) {
            throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
        }
        if (!Looper.getMainLooper().equals(Looper.myLooper())) {
            throw new IllegalThreadStateException("Must call release on the main thread");
        }
        if (--acquired == 0) {
            listener.onResourceReleased(key, this);
        }
    }
}

这两个方法handleResultOnMainThread()方法中出现过,engineResource调用了acquire()方法后,才执行了onEngineJobComplete()方法中,也就是将图片加入了activeResources弱引用集合中。将图片加入弱引用map达到将当前正在使用的图片加入的效果,就出现了acquire和release方法。

当acquired变量大于0时,说明图片正在使用,应该放入activeResources弱引用缓存当中。而经过release()之后,如果acquired值为0了,说明图片不再被使用了,将会调用listener的onResourceReleased()方法来释放资源。

onResourceReleased()

public class Engine implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {

    private final MemoryCache cache;
    private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
    ...    

    @Override
    public void onResourceReleased(Key cacheKey, EngineResource resource) {
        Util.assertMainThread();
        activeResources.remove(cacheKey);
        if (resource.isCacheable()) {
            cache.put(cacheKey, resource);
        } else {
            resourceRecycler.recycle(resource);
        }
    }

    ...
}

这里首先会将缓存图片从activeresources中移除,然后再将它put到LruResourceCache中。否则就进行资源回收。这样就实现了正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存。

硬盘缓存

禁止硬盘缓存可以这样使用:

Glide.with(this)
    .load(url)
    .disCacheStrategy(DiskCacheStrategy.NONE)
    .into(imageview);

disCacheStrategy可以接收四种参数:

  • DiskCacehStrategy.NONE:不缓存任何内容
  • DiskCacehStrategy.SOURCE:只缓存原始图片
  • DiskCacehStrategy.RESULT:只缓存转换后的图片
  • DiskCacehStrategy.ALL:既缓存原始图片,也缓存转换后的图片

decode()

Glide开启线程加载图片后会执行EngineRunnable的run()方法,run方法中又会调用一个decode方法

private Resource<?> decode() throws Exception {
    if (isDecodingFromCache()) {
        return decodeFromCache();
    } else {
        return decodeFromSource();
    }
}

这里有两个方法:

  • decodeFromCache():从硬盘缓存中读取图片
  • decodeFromSource():读取原始图片

Glide会优先从硬盘缓存中读取

decodeFromCache

private Resource<?> decodeFromCache() throws Exception {
    Resource<?> result = null;
    try {
        //DisCacheStrategy.RESULT
        result = decodeJob.decodeResultFromCache();
    } catch (Exception e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Exception decoding result from cache: " + e);
        }
    }
    if (result == null) {
        //DisCacheStrategy.SOURCE
        result = decodeJob.decodeSourceFromCache();
    }
    return result;
}

执行两个方法的区别就在于传入disCacheStrategy方法中的参数

decodeResultFromCache

public Resource<Z> decodeResultFromCache() throws Exception {
    if (!diskCacheStrategy.cacheResult()) {
        return null;
    }
    long startTime = LogTime.getLogTime();
    Resource<T> transformed = loadFromCache(resultKey);
    startTime = LogTime.getLogTime();
    Resource<Z> result = transcode(transformed);
    return result;
}
先调用loadFromCache()从缓存中读取数据,直接将数据解码并返回

decodeSourceFromCache()

public Resource<Z> decodeSourceFromCache() throws Exception {
    if (!diskCacheStrategy.cacheSource()) {
        return null;
    }
    long startTime = LogTime.getLogTime();
    Resource<T> decoded = loadFromCache(resultKey.getOriginalKey());
    return transformEncodeAndTranscode(decoded);
}

先调用loadFromCache()从缓存中读取数据,调用transformEncodeAndTrancode()方法先将数据转换再解码返回。

这两个方法在调用loadFromCache()方法中传入的参数不同。因为Glide缓存的key是由10个参数共同组成的。如果是缓存原始图片,不需要那么多的参数。

硬盘缓存的读取

private Resource<T> loadFromCache(Key key) throws IOException {
    File cacheFile = diskCacheProvider.getDiskCache().get(key);
    if (cacheFile == null) {
        return null;
    }
    Resource<T> result = null;
    try {
        result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);
    } finally {
        if (result == null) {
            diskCacheProvider.getDiskCache().delete(key);
        }
    }
    return result;
}

硬盘缓存的读取在loadFromCache()中。getDishCache()方法获取到Glide自己编写的DiskLruCache工具类的实例,然后调用它的get()方法把缓存key传入,就获得硬盘缓存的文件了。文件不为空,就将其解码成Resource对象后返回就行。

decodeFromSource()

public Resource<Z> decodeFromSource() throws Exception {
    //解析原图片
    Resource<T> decoded = decodeSource();
    //对图片进行转换和转码,再将转换过后的图片写入到硬盘缓存中
    return transformEncodeAndTranscode(decoded);
}

在没有缓存的情况下,会调用decodeFromDecode()方法来读取原始图片。

decodeSource()

private Resource<T> decodeSource() throws Exception {
    Resource<T> decoded = null;
    try {
        long startTime = LogTime.getLogTime();
        //当相应的资源不在缓存中时才调用此方法,异步获取可以从中解码资源的数据
        //fetcher是ImageVideoFetcher HttpUrlFetcher中真正进行网络请求
        final A data = fetcher.loadData(priority);
        if (isCancelled) {
            return null;
        }
        //对数据进行解码
        decoded = decodeFromSourceData(data);
    } finally {
        fetcher.cleanup();
    }
    return decoded;
}

decodeFromSourceData

private Resource<T> decodeFromSourceData(A data) throws IOException {
    final Resource<T> decoded;
    //是否允许缓存原始图片
    if (diskCacheStrategy.cacheSource()) {
        decoded = cacheAndDecodeSourceData(data);
    } else {
        long startTime = LogTime.getLogTime();
        decoded = loadProvider.getSourceDecoder().decode(data, width, height);
    }
    return decoded;
}
private Resource<T> cacheAndDecodeSourceData(A data) throws IOException {
    long startTime = LogTime.getLogTime();
    SourceWriter<A> writer = new SourceWriter<A>(loadProvider.getSourceEncoder(), data);
    diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);
    startTime = LogTime.getLogTime();
    Resource<T> result = loadFromCache(resultKey.getOriginalKey());
    return result;
}

调用getDiskCache()方法来获取DiskLruCache实例,调用put方法写入硬盘缓存。原始图片的缓存key是用的resultKey.getOriginalKey()

参考文章

郭大神Glide系列源码分析

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容