美文网首页
Glide源码分析

Glide源码分析

作者: gczxbb | 来源:发表于2018-06-27 18:03 被阅读91次

    glide已经出来好几年了,在项目加载图片中也经常使用,我自己也打算做一个总结,通过源码阅读和调试,分析一下它的实现方案。glide相关的所有文章都将基于glide 4.5.0源码。本文先通过一段简单代码,介绍Glide图片加载流程,然后再基于各个知识点进行分析。使用方式如下,在gradle中引入。

    compile "com.github.bumptech.glide:glide:4.5.0"
    annotationProcessor "com.github.bumptech.glide:compiler:4.5.0"
    

    添加配置类,继承AppGlideModule,可以在该类中配置GlideBuilder,也可以注册Register,这些配置在创建Glide对象时使用。

    @GlideModule
    public class GlideModuleConfig extends AppGlideModule {
    
        @Override
        public void applyOptions(Context context, GlideBuilder builder) {
    
        }
    
        @Override
        public void registerComponents(Context context, Glide glide, Registry registry) {
            super.registerComponents(context, glide, registry);
        }
    
        @Override
        public boolean isManifestParsingEnabled() {
            return false;
        }
    
    }
    

    为该类添加@GlideModule注解,android apt注解处理器在编译glide源码时,将根据该注解自动生成GlideApp类文件,生成的位置在build/generated/source/apt/debug目录下面。

    glide自动生成的类文件.png 在Activity组件中,我们执行下面的方法,一行代码可以实现图片加载,这是一个最简单的加载方式,包括with,load和into方法,从表面看,就是关联上下文,加载地址和ImageView目标视图。
    GlideApp.with(this).load(url).into(imageView);
    
    显示图片。 屏幕快照.png

    下面从这三个方法开始分析,glide是如何一步步将一个url图片显示到ImageView上的。


    with方法

    首先,该方法返回一个GlideRequests对象,方法参数可以接收View、Context、Activity、Fragment和FragmentActivity。 屏幕快照.png

    这些方法都会调用Glide的with方法,参数也支持这些。

    //GlideApp的with方法。
    public static GlideRequests with(@NonNull View arg0) {
        return (GlideRequests) Glide.with(arg0);
    }
    
    //Glide的with方法。
    public static RequestManager with(@NonNull View view) {
        return getRetriever(view.getContext()).get(view);
    }
    

    在调用Glide的getRetriever方法时,传入Context, 视图获取它的Context,Fragment获取它的Activity,其他直接传入。返回一个RequestManagerRetriever对象,它在Glide单例对象内部。

    private static RequestManagerRetriever getRetriever(@Nullable Context context) {
        return Glide.get(context).getRequestManagerRetriever();
    }
    

    下面是利用Context来获取Glide单例,完成配置初始化,可以看出,Glide框架也有一个可配置的单例对象控制全局,其他开源框架比如Okhttp也类似。

    Glide类内部静态私有对象
    private static volatile Glide glide
    
    @NonNull
    public static Glide get(@NonNull Context context) {//context是View的Context
        if (glide == null) {
            synchronized (Glide.class) {
                if (glide == null) {
                    checkAndInitializeGlide(context);//只初始化一次,创建glide对象
                }
            }
        }
        return glide;
    }
    

    Glide初始化一次,checkAndInitializeGlide方法,调用initializeGlide方法,创建一个GlideBuilder建造者对象,GlideBuilder向用户开放,添加配置。

    private static void initializeGlide(@NonNull Context context) {
        initializeGlide(context, new GlideBuilder());
    }
    //创建Glide。
    @SuppressWarnings("deprecation")
    private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
        //获取全局Context。
        Context applicationContext = context.getApplicationContext();
        //创建一个GeneratedAppGlideModuleImpl对象。
        GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
        List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
        if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
            manifestModules = new ManifestParser(applicationContext).parse();
        }
        if (annotationGeneratedModule != null
                && !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
            Set<Class<?>> excludedModuleClasses =
                    annotationGeneratedModule.getExcludedModuleClasses();
            Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
            while (iterator.hasNext()) {
                com.bumptech.glide.module.GlideModule current = iterator.next();
                if (!excludedModuleClasses.contains(current.getClass())) {
                    continue;
                }
                iterator.remove();
            }
        }
        //获取RequestManagerFactory工厂。
        RequestManagerRetriever.RequestManagerFactory factory =
                annotationGeneratedModule != null
                        ? annotationGeneratedModule.getRequestManagerFactory() : null;
        builder.setRequestManagerFactory(factory);
        for (com.bumptech.glide.module.GlideModule module : manifestModules) {
            module.applyOptions(applicationContext, builder);
        }
        if (annotationGeneratedModule != null) {
            //用户初始化builder模块,在AppGlideModule的继承类applyOptions方法中实现。
            annotationGeneratedModule.applyOptions(applicationContext, builder);
        }
        //建造者方法创建Glide对象
        Glide glide = builder.build(applicationContext);
        for (com.bumptech.glide.module.GlideModule module : manifestModules) {
            module.registerComponents(applicationContext, glide, glide.registry);
        }
        if (annotationGeneratedModule != null) {
            annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
        }
        applicationContext.registerComponentCallbacks(glide);
        Glide.glide = glide;
    }
    

    首先,创建一个com.bumptech.glide.GeneratedAppGlideModuleImpl实例对象,它也是在编译时自动生成的Java类,继承GeneratedAppGlideModule,继承AppGlideModule。
    GeneratedAppGlideModuleImpl的内部包含AppGlideModule,它就是我们在文章开头自定义的GlideModuleConfig配置类,有同一个父类AppGlideModule。
    然后,执行GeneratedAppGlideModuleImpl的applyOptions方法和registerComponents方法,进入我们自定义配置类的这两个方法,将GlideBuilder暴漏出来,设置配置项。
    最后,GlideBuilder的build方法创建Glide实例。
    我们再回到上面的getRetriever方法,该方法将返回Glide内部引用的RequestManagerRetriever,下面是它在GlideBuilder#build方法的初始化。

    RequestManagerRetriever requestManagerRetriever =
            new RequestManagerRetriever(requestManagerFactory);
    

    再回到上面Glide的with方法,如果之前已经创建过Glide实例,将直接获取内部RequestManagerRetriever,然后,通过它的get方法,获取RequestManager请求管理器。

    public RequestManager get(@NonNull View view) {
        //非主线程时
        if (Util.isOnBackgroundThread()) {
            return get(view.getContext().getApplicationContext());
        }
        Activity activity = findActivity(view.getContext());
        if (activity == null) {
            return get(view.getContext().getApplicationContext());
        }
        if (activity instanceof FragmentActivity) {
            Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
            return fragment != null ? get(fragment) : get(activity);
        }
        android.app.Fragment fragment = findFragment(view, activity);
        if (fragment == null) {
            return get(activity);
        }
        return get(fragment);
    }
    

    Glide的with方法支持各种参数,get方法也一样,支持View,Fragment,Activity,Context参数。以参数View为例,若是非主线程调用或该View的Activity已经是空,根据全局Context去get。其他情况,根据Activity或Fragment去get。
    最终,有三个地方利用工厂去创建RequestManager。全局Context,获取它内部的RequestManager对象。

    private RequestManager getApplicationManager(@NonNull Context context) {
        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;
    }
    

    如果过是主线程Activity或Fragment组件加载,且他们存在时,调用fragmentGet或supportFragmentGet方法创建,这两种都是将创建的RequestManager对象保存在RequestManagerFragment和SupportRequestManagerFragment中。

    @NonNull
    private RequestManager fragmentGet(@NonNull Context context,
                                       @NonNull android.app.FragmentManager fm,
                                       @Nullable android.app.Fragment parentHint) {
        RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            Glide glide = Glide.get(context);
            requestManager =
                    factory.build(
                            glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }
    
    @NonNull
    private RequestManager supportFragmentGet(@NonNull Context context, @NonNull FragmentManager fm,
                                              @Nullable Fragment parentHint) {
         //parentHint是加载图片的那个Fragment。
        SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            Glide glide = Glide.get(context);
            requestManager =
                    factory.build(
                            glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }
    

    RequestManager由RequestManagerFactory工厂创建,该工厂在GlideBuilder,上面的initializeGlide方法,GeneratedAppGlideModuleImpl重写了工厂获取类,GeneratedRequestManagerFactory工厂类,自动生成的Java类。

    @Override
    GeneratedRequestManagerFactory getRequestManagerFactory() {
        return new GeneratedRequestManagerFactory();
    }
    
    final class GeneratedRequestManagerFactory implements RequestManagerRetriever.RequestManagerFactory {
      @Override
      public RequestManager build(Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode,
          Context context) {
        return new GlideRequests(glide, lifecycle, treeNode, context);
      }
    }
    
    

    该工厂获取的是一个GlideRequests对象,它继承RequestManager。回到GlideApp的with方法,将它转换成GlideRequests对象,GlideRequests也是自动生成的类。

    到这里,with方法就结束了,总之,就是根据当前请求的组件或视图上下文,返回一个GlideRequests请求实体管理者。


    load方法

    前面的with方法,得到的是一个GlideRequests对象,下面我们调用这个对象的load方法,它支持多种数据源,如url、文件、Bitmap、字节数组、Drawable、资源Id。 加载源.png

    我们以url进行分析,看一下GlideRequest的load(url)方法做了哪些事情。它的父类是RequestManager,调用父类的load方法。

    //GlideRequest方法
    public GlideRequest<Drawable> load(@Nullable String arg0) {
        return (GlideRequest<Drawable>) super.load(arg0);
    }
    //RequestManager方法
    public RequestBuilder<Drawable> load(@Nullable String string) {
        return asDrawable().load(string);
    }
    

    RequestManager#asDrawable方法。创建一个RequestBuilder对象,代表一个请求实体。

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

    首先,<ResourceType>资源类型resourceClass是Drawable类型。请求实体构造方法传入glide对象,请求管理器,资源类型(Drawable类型)和Context。
    类似方法还包括asGif,资源类型GifDrawable,asBitmap,资源类型Bitmap,asFile,资源类型File。
    然后,创建RequestBuilder请求实体,该实体泛型对应具体资源类型。回到load方法,调用RequestBuilder<Drawable>实体的load方法。

    public RequestBuilder<TranscodeType> load(@Nullable String string) {
        return loadGeneric(string);
    }
    
    @NonNull
    private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model){
        this.model = model;
        isModelSet = true;
        return this;
    }
    

    该方法返回RequestBuilder<Drawable>自己本身,主要目的是将加载资源保存在内部model对象中,在我们举例中保存String类型的url。
    再次回到最初GlideRequest的load方法,将返回的请求实体类型转换成GlideRequest<Drawable>。GlideRequest继承RequestBuilder。GlideRequest也是自动生成的Java类。

    到这里,load方法也介绍完了,它比较简单,总的来说,就是由GlideRequests实体管理者,根据请求资源的地址创建一个具体的GlideRequest请求实体。


    into方法

    glide简单示例三个方法中的最后一个,调用的是GlideRequest<Drawable>的into方法,父类RequestBuilder的into方法,它的入参是ImageView。

    public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
        RequestOptions requestOptions = this.requestOptions;
        if (!requestOptions.isTransformationSet()
                && requestOptions.isTransformationAllowed()
                && view.getScaleType() != null) {
            switch (view.getScaleType()) {
                case CENTER_CROP:
                    requestOptions = requestOptions.clone().optionalCenterCrop();
                    break;
                case CENTER_INSIDE:
                    requestOptions = requestOptions.clone().optionalCenterInside();
                    break;
                 ...
            }
        }
        return into(
                glideContext.buildImageViewTarget(view, transcodeClass),
                /*targetListener=*/ null,
                requestOptions);
    }
    

    首先,根据ImageView的ScaleType类型,设置RequestOptions的配置项,ScaleType类型判断相关代码没有全部贴出来。
    然后,调用GlideContext的buildImageViewTarget方法,上面介绍过,在请求实体RequestBuilder构造方法,将Glide单例传入,每个请求实体内部引用GlideContext。
    buildImageViewTarget方法,创建一个ViewTarget<ImageView, X>,这里的传入的transcodeClasss是Drawable类型,通过内部的ImageViewTargetFactory工厂类,创建的ViewTarget有两种类型,这里是DrawableImageViewTarget。此外,如果资源类型transcodeClasss是Bitmap,创建的是BitmapImageViewTarget类型。
    这个transcodeClass类型就是我们创建RequestBuilder对象时,传入的resourceClass类型,即Drawable。根据不同的class类型,创建不同的ViewTarget对象。
    最后,在成功创建ViewTarget后,调用RequestBuilder<Drawable>的另一个into方法。

    private <Y extends Target<TranscodeType>> Y into(
            @NonNull Y target,
            @Nullable RequestListener<TranscodeType> targetListener,
            @NonNull RequestOptions options) {
        //在into之前,调用load方法设置model,并设置isModelSet为true。
        if (!isModelSet) {
            throw new IllegalArgumentException("You must call #load() before calling #into()");
        }
        options = options.autoClone();
        Request request = buildRequest(target, targetListener, options);
        ...
        requestManager.clear(target);
        target.setRequest(request);
        requestManager.track(target, request);
        return target;
    }
    

    首先,调用buildRequest方法,创建一个Request请求,它是一个接口,代表为Target加载资源的请求,将该请求设置到ViewTarget内部。下面看一下创建一个真正的请求实现类的方法。

    private Request buildRequest(
          Target<TranscodeType> target,
          @Nullable RequestListener<TranscodeType> targetListener,
          RequestOptions requestOptions) {
       //调用内部buildRequestRecursive方法,将requestOptions提取传入
        return buildRequestRecursive(
            target,
            targetListener,
            ...其他参数);
    }
    

    传入参数包括ViewTarget,RequestListener和RequestOptions。Request创建的代码较多,不详细分析了。还是按照上面最简单的图片加载方式示例,只有with,load,into三个方法,调试一下,最后调用的位置是RequestBuilder的obtainRequest方法,创建SingleRequest。

    private Request obtainRequest(
          Target<TranscodeType> target,
          RequestListener<TranscodeType> targetListener,
          RequestOptions requestOptions,
          RequestCoordinator requestCoordinator,
          TransitionOptions<?, ? super TranscodeType> transitionOptions,
          Priority priority,
          int overrideWidth,
          int overrideHeight) {
        return SingleRequest.obtain(
            context,
            glideContext,
            ...//参数较多);
    }
    

    通过SingleRequest的obain静态方法创建实例,实例的init方法初始化传入的这些参数。
    创建SingleRequest实例后,再回到RequestBuilder<Drawable>的into方法,然后,调用请求管理器的track方法,上面介绍过,在请求实体RequestBuilder构造方法,将请求管理器传入,每个请求实体内部引用RequestManager。

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

    TargetTracker中存储一个Target的集合,将Target加入集合,然后执行RequestTracker的runRequest方法。

    public void runRequest(Request request) {
        requests.add(request);
        if (!isPaused) {
          request.begin();
        } else {
          pendingRequests.add(request);
        }
    }
    

    将请求加入内部Set集合,如果isPaused暂停标志,将请求加入pendingRequests列表,RequestTracker是一个请求追踪器。在未暂停情况下,调用SingleRequest的begin方法开始请求。

    public void begin() {
        startTime = LogTime.getLogTime();
        if (model == null) {
            ...
            onLoadFailed(new GlideException("Received null model"), logLevel);
            return;
        }
    
        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());
        }
    }
    

    在前面介绍过的创建RequestBuilder<Drawable>时,会将加载资源(网络是url)保存在内部model对象中,该对象后初始化给SingleRequest,因此,如果是空,异常onLoadFailed方法。
    初始状态是Status.PENDING,若已经改变成COMPLETE,直接回调返回。否则,将状态变更WAITING_FOR_SIZE。
    最后,看一下onSizeReady方法,该方法通过SingleRequest内部的加载引擎Engine去load加载,返回一个状态LoadStatus。

    public void onSizeReady(int width, int height) {
        stateVerifier.throwIfRecycled();
        //状态必须是WAITING_FOR_SIZE。
        if (status != Status.WAITING_FOR_SIZE) {
            return;
        }
        status = Status.RUNNING;//状态更新RUNNING
        float sizeMultiplier = requestOptions.getSizeMultiplier();
        this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
        this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
        loadStatus = engine.load(
                glideContext,
                model,
                requestOptions.getSignature(),
                this.width,
                this.height,...);//load参数较多
        if (status != Status.RUNNING) {
            loadStatus = null;
        }
    }
    

    该方法主要加载引擎Engine的load方法,传入的参数大部分是请求配置参数。

    public <R> LoadStatus load(
            GlideContext glideContext,
            Object model,
            Key signature,
            int width,
            int height,
            ...
            ResourceCallback cb) {
        long startTime = LogTime.getLogTime();
        EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
                resourceClass, transcodeClass, options);
        EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
        if (active != null) {
            cb.onResourceReady(active, DataSource.MEMORY_CACHE);
            return null;
        }
        EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
        if (cached != null) {
            cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
            return null;
        }
        EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
        if (current != null) {
            current.addCallback(cb);
            return new LoadStatus(cb, current);
        }
        EngineJob<R> engineJob =
                engineJobFactory.build(
                        key,
                        isMemoryCacheable,
                        useUnlimitedSourceExecutorPool,
                        useAnimationPool,
                        onlyRetrieveFromCache);
        DecodeJob<R> decodeJob =
                decodeJobFactory.build(
                        glideContext,
                        model,
                        key,
                        ...
                        options,
                        engineJob);
        jobs.put(key, engineJob);
        engineJob.addCallback(cb);
        engineJob.start(decodeJob);
    
        return new LoadStatus(cb, engineJob);
    }
    

    创建一个EngineKey,若支持内存缓存,首先,loadFromActiveResources方法,根据key,在ActiveResources中获取资源EngineResource。其次,loadFromCache方法,同样支持内存缓存。以上这两种情况都是从内存获取数据,onResourceReady回调表明来源是MEMORY_CACHE。
    若内存缓存中未找到,就需要从磁盘缓存或者网络中获取了。EngineJobFactory工厂和DecodeJobFactory工厂分别创建两个Job,他们是EngineJob和DecodeJob。下面是任务开始,EngineJob的start方法。

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

    willDecodeFromCache方法,是否从Disk缓存中获取,内部有四个GlideExecutor线程池,根据条件,选择一个线程池执行任务。DecodeJob是一个Runnable任务,该任务获取图片,可以从资源中,文件缓存,或者网络中获取。

    本文先介绍到这里,文章篇幅过大,关于DecodeJob任务执行的过程下次介绍。


    任重而道远

    相关文章

      网友评论

          本文标题:Glide源码分析

          本文链接:https://www.haomeiwen.com/subject/jcoahftx.html