Glide源码分析

来源:互联网 发布:端口定义 编辑:程序博客网 时间:2024/05/24 06:20

导读

本文将尝试梳理Glide 3.7.0版本常用方法的源码实现,关于Glide的使用方法网上已经有足够多的资料可供查阅,因此本文默认阅读者对于Glide的使用方法已无障碍。第一次尝试将自己的学习结果整理成文字,有不足之处和建议请多多指出。

实例构建

    public static Glide get(Context context) {        if (glide == null) {            synchronized (Glide.class) {                if (glide == null) {                    Context applicationContext = context.getApplicationContext();                    List<GlideModule> modules = new ManifestParser(applicationContext).parse();                    GlideBuilder builder = new GlideBuilder(applicationContext);                    for (GlideModule module : modules) {                        module.applyOptions(applicationContext, builder);                    }                    glide = builder.createGlide();                    for (GlideModule module : modules) {                        module.registerComponents(applicationContext, glide);                    }                }            }        }        return glide;    }

Glide使用了单例模式构造了全局唯一的实例对象,因此配置文件以及动态配置的参数也是全局生效的。在ManifestParser中通过PackageManager获取了所有meta-data标签中value=”GlideModule”的配置,并通过反射构造了GlideModule实例。然后将Builder对象传入GlideModule实例进行配置并创建Glide实例对象,最后注册后续需要使用的Component。具体参数的作用将在后续详细介绍。

with()

with方法一共有五个重载

public static RequestManager with(Activity activity)public static RequestManager with(Context context)public static RequestManager with(android.app.Fragment fragment)public static RequestManager with(Fragment fragment)public static RequestManager with(FragmentActivity activity)

五个重载均会调用RequestManagerRetriever中对应的get方法生成RequestManager。其中当调用线程为后台线程或者Context对象为ApplicationContext时会返回无生命周期管理的RequestManager。否则最终会调用

RequestManager fragmentGet(Context context, android.app.FragmentManager fm)RequestManager supportFragmentGet(Context context, FragmentManager fm)

这两者的差别不影响对于Glide框架的了解,因此这里用fragmentGet来进行分析。

    RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {        RequestManagerFragment current = getRequestManagerFragment(fm);        RequestManager requestManager = current.getRequestManager();        if (requestManager == null) {            requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());            current.setRequestManager(requestManager);        }        return requestManager;    }    RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {        RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);        if (current == null) {            current = pendingRequestManagerFragments.get(fm);            if (current == null) {                current = new RequestManagerFragment();                pendingRequestManagerFragments.put(fm, current);                fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();                handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();            }        }        return current;    }

在这里生成了一个没有视图RequestManagerFragment,并将其add到了FragmentManager中用来监听当前Activity的生命周期,这也就解释了为什么Glide会根据当前Activity状态来取消与继续请求。

load()

load方法有很多个重载,但是最终会先后调用

private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass)public DrawableRequestBuilder<ModelType> load(ModelType model)

先看loadGeneric

    private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {        ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);        ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =                Glide.buildFileDescriptorModelLoader(modelClass, context);        return optionsApplier.apply(                new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context, glide, requestTracker, lifecycle, optionsApplier));    }

在optionsApplier.apply()中其实就是返回了DrawableTypeRequest对象本身,这个对象也就是真正用来发起每一条请求的对象,每一条请求的个性设置也都是存储在DrawableTypeRequest对象中。而streamModelLoader fileDescriptorModelLoader 就是用我们在Glide实例生成时候注册的ModelLoaderFactory生成的ModelLoader实例,比如我们常用的StreamStringLoader。

再看load方法就很简单了,仅仅是将请求的model存储了起来,

    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {        this.model = model;        isModelSet = true;        return this;    }

placeholder() error() override() thumbnail() transform() animate()

这些方法都仅仅是将参数存储起来,后面在需要的时候读取,并没有真正的应用。同时crossFade()等方法最终调用的是animat(), fitCenter()等方法调用的是transform()

asBitmap() asGif()

这两个方法分别返回了BitmapRequestBuilder GifTypeRequestBuilder对象,并将自身的设置传递给了新生成的对象,主要差异是解码时的逻辑不同

into()

into()方法是真正调用加载逻辑的方法,上面设置的各种参数与接口实例将在这里被应用。

    public Target<TranscodeType> into(ImageView view) {        return into(glide.buildImageViewTarget(view, transcodeClass));    }    public <Y extends Target<TranscodeType>> Y into(Y target) {        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;    }

通过代码可以看到ImageView最终会被封装为Target对象,根据不同的RequestBuilder对象默认提供三种封装

  • GlideDrawableImageViewTarget
  • BitmapImageViewTarget
  • DrawableImageViewTarget

GlideDrawableImageViewTarget主要是对Gif的动画播放次数进行了控制,另外两个很好理解。

因为接下来的方法还涉及到缓存策略,并且回调较多,粘贴代码会显得过于冗长,因此将减少代码的粘贴,有兴趣的可以自己下载源码进行阅读

再来看参数为Target的重载,首先停止当前Target中已经存在的Request以节约资源。然后根据当前参数构造当前的Request对象,主要差异在于是否有缩略图。最后启动Request对象,并监听当前Context对象的生命周期。

runRequest方法根据当前上下文的状态调用了Request的begin方法。在begin方法中对View注册了OnPreDrawListener监听来确定View的大小,并尝试从当前正在被使用的EngineCache中获取Resource对象,如果获取失败则在磁盘IO线程池中启动请求,并设置placeholder。

请求启动后首先会尝试从磁盘读取缓存文件并进行解码,如果缓存不存在或者解码失败将判断一次请求是否被取消,如果未取消则进入原始资源读取逻辑。原始资源的获取会交给前面在RequestManager.loadGeneric()中构造的DataFetcher对象,获得资源后根据设置决定是否将原始资源写盘。最后进入解码流程。

不论是磁盘还是原始数据解码出来的图片,接下来会将在RequestBuilder中设置的transform应用上,并根据设置决定是否将最终结果写入磁盘。

最后如果还调用过asBtimap() asGif()或者设置了自定义的ResourceTranscoder,将在此时对Resouce的类型进行转换并最终返回,并调用onResourceReady方法显示动画效果并展示图片,如果涉及到缩略图将可能存在下一次动画和更新(之所以说可能是因为如果原图返回的比缩略图更快将会丢失缩略图的更新)。

至此一次常用的Glide的完整请求已经结束,接下来打算整理一下Glide的缓存策略,大家下篇文章和评论区见~

1 0