Glide源码分析

版本号:4.9.0

一.基本概念

1.Model

数据来源,不管是imgURL、资源文件还是本地文件,在Glide中封装为Model

2.Data

原始数据,获取Model之后,处理成原始数据,一般就是输入流,在Glide中封装为Data

3.Resource

解码之后的资源,比如将输入流解码成Bitmap

4.TransformedResource

Resource变换之后的资源,比如:裁剪。

5.TranscodedResource

转码之后的资源,将解码之后的资源转换成统一的格式。

5.Target

图片显示的目标。

加载流程

二.基本使用

Glide.with(this)
    .load(imgUrl)
    .placeholder(R.mipmap.ic_launcher)
    .error(R.mipmap.ic_launcher)
    .override(300,300)//指定图片的尺寸
    .skipMemoryCache(true)//不使用内存缓存
    .diskCacheStrategy(DiskCacheStrategy.ALL)//磁盘缓存:缓存所有版本的
    .centerCrop() //设置缩放类型
    .priority(Priority.HIGH)//指定优先级,尽可能处理优先级高的
    .into(imageView)

最核心的方法其实就是:with()、load()、into(),创建请求对象,指定数据源,设置显示目标。其他的方法都是一些图片的设置和缓存设置等。下面分别来看一下这三个方法:

1.with()方法

该方法有很多的重载,传入的参数分别是context、activity、fragmentActivity、fragment,下面以参数为context的方法为为例分析:

@NonNull
public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
}

with()方法返回一个RequestManager对象,该对象是请求管理的对象,是通过RequestManagerRetrieverget()方法获取的,RequestManagerRetriever就是用来生产RequestManager对象的。下面看一下get()方法:

@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) {
        return get(((ContextWrapper) context).getBaseContext());
      }
    }
    return getApplicationManager(context);
}

如果不是在UI线程或者context是Application,就会调用getApplicationManager(context);方法,否则就会对context分类调用对应的方法。getApplicationManager(context);方法就是通过Glide创建一个RequestManager对象。

下面看一下参数为activity的方法:

public class RequestManagerRetriever implements Handler.Callback{
    @SuppressWarnings("deprecation")
    @NonNull
    public RequestManager get(@NonNull Activity activity) {
        if (Util.isOnBackgroundThread()) {
          //如果是在子线程中调用的,使用的还是Application的上下文
          return get(activity.getApplicationContext());
        } else {
          assertNotDestroyed(activity);
          android.app.FragmentManager fm = activity.getFragmentManager();
          //通过fragmentGet()方法创建RequestManager对象
          return fragmentGet(
              activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
        }
    }

    @SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"})
    @Deprecated
    @NonNull
    private RequestManager fragmentGet(@NonNull Context context,
        @NonNull android.app.FragmentManager fm,
        @Nullable android.app.Fragment parentHint,
        boolean isParentVisible) {
        //创建一个没有UI的Fragment,Fragment可以监听Activity的生命周期,所以Glide通过该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;
    }
}

Glide是没法直接监听Activity的生命周期的,这里创建了一个没有UI的Fragment,通过它来间接的监听对应的生命周期,下面看一下是如果实现监听的:

public class RequestManagerFragment extends Fragment {
    //ActivityFragmentLifecycle类是生命周期回调的管理类
    private final ActivityFragmentLifecycle lifecycle;
    ......
    public RequestManagerFragment() {
        this(new ActivityFragmentLifecycle());
    }
    ......
}

ActivityFragmentLifecycle它实现了LifeCycle接口,会将LifecycleListener的接口加入到ActivityFragmentLifecycle类中的Set集合中,当RequestManagerFragment的生命周期的方法触发时,会调用ActivityFragmentLifeCycle的相应方法。

public class RequestManager implements LifecycleListener,
    ModelTypes<RequestBuilder<Drawable>> {

    RequestManager(Glide glide,Lifecycle lifecycle, RequestManagerTreeNode treeNode,
          RequestTracker requestTracker,ConnectivityMonitorFactory factory,Context context) {
        this.glide = glide;
        this.lifecycle = lifecycle;
        this.treeNode = treeNode;
        this.requestTracker = requestTracker;
        this.context = context;
        ......
        if (Util.isOnBackgroundThread()) {
          mainHandler.post(addSelfToLifecycle);
        } else {
          lifecycle.addListener(this);
        }
        lifecycle.addListener(connectivityMonitor);
        ......
        glide.registerRequestManager(this);
    }
}

RequestManager实现了LifeCycleListener接口,在RequestManager的构造方法中会将创建的RequestManager对象加入到ActivityFragmentLifeCycle的管理LifeCycleListener接口的集合中去,所以当ActivityFragmentLifeCycle的相应的方法调用时就会调用RequestManager对象的相应方法

2.load()方法

可以看到load()方法也有很多的重载,因为数据的来源不同。下面以参数为Uri的方法为例分析:

public RequestBuilder<Drawable> load(@Nullable Uri uri) {
    return asDrawable().load(uri);
}

asDrawable()方法就是创建一个RequestBuilder实例,然后调用该实例的load()方法,该方法最终会调用loadGeneric()方法,该方法的作用就是给RequestBuilder的成员变量private Object model;赋值。然后返回this,形成链式调用。下面看一下RequestBuilder的成员变量:

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
    // Used in generated subclasses
    protected static final RequestOptions DOWNLOAD_ONLY_OPTIONS =
      new RequestOptions().diskCacheStrategy(DiskCacheStrategy.DATA).priority(Priority.LOW)
          .skipMemoryCache(true);

    private final Context context;
    private final RequestManager requestManager;
    private final Class<TranscodeType> transcodeClass;
    private final Glide glide;
    private final GlideContext glideContext;

    @NonNull
    @SuppressWarnings("unchecked")
    private TransitionOptions<?, ? super TranscodeType> transitionOptions;

    @Nullable private Object model;
    // model may occasionally be null, so to enforce that load() was called, put a boolean rather
    // than relying on model not to be null.
    @Nullable private List<RequestListener<TranscodeType>> requestListeners;
    @Nullable private RequestBuilder<TranscodeType> thumbnailBuilder;
    @Nullable private RequestBuilder<TranscodeType> errorBuilder;
    @Nullable private Float thumbSizeMultiplier;
    private boolean isDefaultTransitionOptionsSet = true;
    private boolean isModelSet;
    private boolean isThumbnailBuilt;
}

//RequestBuilder的父类
public abstract class BaseRequestOptions<T extends BaseRequestOptions<T>> implements Cloneable {
    ......
    private int fields;
    private float sizeMultiplier = 1f;
    @NonNull
    private DiskCacheStrategy diskCacheStrategy = DiskCacheStrategy.AUTOMATIC;
    @NonNull
    private Priority priority = Priority.NORMAL;
    @Nullable
    private Drawable errorPlaceholder;
    private int errorId;
    @Nullable
    private Drawable placeholderDrawable;
    private int placeholderId;
    private boolean isCacheable = true;
    private int overrideHeight = UNSET;
    private int overrideWidth = UNSET;
    @NonNull
    private Key signature = EmptySignature.obtain();
    private boolean isTransformationRequired;
    private boolean isTransformationAllowed = true;
    @Nullable
    private Drawable fallbackDrawable;
    private int fallbackId;
    @NonNull
    private Options options = new Options();
    @NonNull
    private Map<Class<?>, Transformation<?>> transformations = new CachedHashCodeArrayMap<>();
    @NonNull
    private Class<?> resourceClass = Object.class;
    private boolean isLocked;
    @Nullable
    private Resources.Theme theme;
    private boolean isAutoCloneEnabled;
    private boolean useUnlimitedSourceGeneratorsPool;
    private boolean onlyRetrieveFromCache;
    private boolean isScaleOnlyOrNoTransform = true;
    private boolean useAnimationPool;
    ......
}

RequestBuilder及其父类的成员变量可以看到,关于Glide的配置的属性都在这里面,比如:overrideHeight 、overrideWidth 、placeholderDrawable、priority等。在该类的构造方法中会对这些属性有默认的初始化赋值。

3.into()方法

在上面配置好imageUrl和一些参数设置之后调用into()方法,就可以实现显示图片的功能了。下面看一个该方法的代码:

@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) {
      根据ImageView的scaleType属性来给对应的配置属性赋值
      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.
      }
    }
    //调用into()方法,传入了一个Target参数
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
}

从上述代码中可以看出,into()方法必须要主线程中调用,否则抛出异常。在该方法中将传入的ImageView对象封装成了Target对象通过glideContext.buildImageViewTarget(view, transcodeClass)方法,下面就看一下该方法的具体实现:

public class ImageViewTargetFactory {
  @NonNull
  @SuppressWarnings("unchecked")
  public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
      @NonNull Class<Z> clazz) {
    if (Bitmap.class.equals(clazz)) {
      //如果调用了asBitmap()方法
      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)");
    }
  }
}

BitmapImageViewTarget为例,就是对ImageView封装了一层,调用对应的方法其实就是调用ImageView的相应方法。比如如下所示:

public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {
    ......
    @Override
    protected void setResource(Bitmap resource) {
        view.setImageBitmap(resource);
    }
    ......
}

创建Target实例以后就会调用参数为该实例的into()方法,下面看一下该方法的实现:

private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,Executor callbackExecutor) {
    ......
    //构建请求
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
    //获取上一个请求
    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      //如果是相同的请求直接复用
      request.recycle();
      ......
      if (!Preconditions.checkNotNull(previous).isRunning()) {
      ......
        previous.begin();
      }
      return target;
    }
    requestManager.clear(target);
    //target与request绑定,通过View.setTag(obj)
    target.setRequest(request);
    requestManager.track(target, request);
    return target;
}

下面继续看一下requestManager.track(target, request);方法:

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

最终调用requestTracker.runRequest(request);方法来执行请求,继续看一下该请求的实现:

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

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();来开始请求:

@Override
public synchronized void begin() {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    ......
    if (status == Status.RUNNING) {
      throw new IllegalArgumentException("Cannot restart a running request");
    }
    if (status == Status.COMPLETE) {
      onResourceReady(resource, DataSource.MEMORY_CACHE);
      return;
    }
    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());
    }
    ......
}

如果设置了图片的大小override(300,300),图片的宽高就使用没有,没有设置的话,就使用ImageView的宽高target.getSize(),最终都会调用onSizeReady()方法来设置图片大小以及开始真正的请求。

public synchronized void onSizeReady(int width, int height) {
    ......
    status = Status.RUNNING;
    float sizeMultiplier = requestOptions.getSizeMultiplier();
    this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
    this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
    ......
    loadStatus =
        //开始真正的请求
        engine.load(参数省略...);
    ......
}

通过engineload()方法来执行请求:

public synchronized <R> LoadStatus load(参数省略...){
    ......
    engineJob.addCallback(cb, callbackExecutor);
    engineJob.start(decodeJob);
    ......
}

public synchronized void start(DecodeJob<R> decodeJob) {
    //decodeJob是一个Runable
    this.decodeJob = decodeJob;
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    executor.execute(decodeJob);
}

KotlinAndroid

EShop

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容