Glide解析

前言

       移动应用几乎都有图片加载的需求,很多时候需要从远程加载,有时也需要从本地加载,以前都往往是自己实现,这就需要考虑各种各样的情况,比如缓存策略,需要综合考虑内存使用,不同的图片有不同时间,不同空间的缓存策略,其次是加载策略,是原图加载,还是需要裁剪,是一次生成多种尺寸的缩略,还是不生成,有时还需要考虑网络状况来加载更小尺寸的图。加载的图片是否是gif,还需要对图片的展示进程处理。有时还是从assert中加载的,或者从sd卡中加载的,不同的加载路径怎么拆分,怎么才有更好的代码设计,这都是对代码能力的一个考量。
       之后就逐步出现了很多的图片加载框架,这些框架现在也应该使用在众多的APP中,他们实现的更全面,功能更强大,有良好的扩展能力。把程序员从众多的逻辑中解放出来,虽然使用更简单了,但是我们要知其然也要知其所以然。接下来我们就梳理一下Glide的加载流程。

使用

       在分析之前我们先来看一个使用的样例。这里我们来加载一个远程url的图像,并且生成为圆形头像。

/**
 * 加载圆形头像
 *
 * @param context
 * @param url
 * @param view
 */
public static final void loadCircleImage(Context context, String url, ImageView view, int defaultAvatar) {
    Glide.with(context).load(url).centerCrop().bitmapTransform(new
            CropCircleTransformation(context)).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder
            (defaultAvatar).error(defaultAvatar).into(view);
}

这里占位图与错误图都使用一个默认的资源。上面的代码的意思很清楚明了,就是加载地址为url的图片,并缩放类型为CenterCrop,之后transformation为圆形,缓存策略为所有尺寸缓存,占位图,错误图都指定为defaultAvatar,最终展示到传入的view。

代码跟踪

       这里我们就解析上面url的加载类型,其他的都是类似的。

第一步Glide.with(context)

public static RequestManager with(Context context) {
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    return retriever.get(context);
}

       这里主要是获得一个RequestManager,首先获取一个RequestManagerRetriever,RequestManagerRetriever是一个单例,get就是获取RequestManagerRetriever的实例,这更加context获取一个RequestManager,retriever.get(context)代码如下:

public RequestManager get(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);
}

public RequestManager get(FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
        return get(activity.getApplicationContext());
    } else {
        assertNotDestroyed(activity);
        FragmentManager fm = activity.getSupportFragmentManager();
        return supportFragmentGet(activity, fm);
    }
}

public RequestManager get(Fragment fragment) {
    if (fragment.getActivity() == null) {
        throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
    }
    if (Util.isOnBackgroundThread()) {
        return get(fragment.getActivity().getApplicationContext());
    } else {
        FragmentManager fm = fragment.getChildFragmentManager();
        return supportFragmentGet(fragment.getActivity(), fm);
    }
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public RequestManager get(Activity activity) {
    if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        return get(activity.getApplicationContext());
    } else {
        assertNotDestroyed(activity);
        android.app.FragmentManager fm = activity.getFragmentManager();
        return fragmentGet(activity, fm);
    }
}

       上面的代码只是根据不同的context来创建不同的requestManager,如果不在u线程,context不是Application则全局创建一个,比如这里传入的Context类型为Activity,且在ui线程,那会加载参数类型为Activity的get函数,这里又会进入的fragmentGet函数:

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
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;
}

       这里首先根据tag "com.bumptech.glide.manager"获取Fragment,这里的Fragment的类型RequestManagerFragment,获取不到会创建一个家人的FragmentManager中,这里假设首次调用,因此requestManager为空,则会创建一个并且设置到Fragment,RequestManager构造如下:

public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
    this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());
}

RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
        RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
    this.context = context.getApplicationContext();
    this.lifecycle = lifecycle;
    this.treeNode = treeNode;
    this.requestTracker = requestTracker;
    this.glide = Glide.get(context);
    this.optionsApplier = new OptionsApplier();

    ConnectivityMonitor connectivityMonitor = factory.build(context,
            new RequestManagerConnectivityListener(requestTracker));

    // If we're the application level request manager, we may be created on a background thread. In that case we
    // cannot risk synchronously pausing or resuming requests, so we hack around the issue by delaying adding
    // ourselves as a lifecycle listener by posting to the main thread. This should be entirely safe.
    if (Util.isOnBackgroundThread()) {
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                lifecycle.addListener(RequestManager.this);
            }
        });
    } else {
        lifecycle.addListener(this);
    }
    lifecycle.addListener(connectivityMonitor);
}

       传入的参数为Context,lifecycle,已经请求树结点,这个就不解析, 之后三参数的调用了无参数的构造函数,构造了一个请求跟踪器RequestTracker,并在在lifecycle中加入了网络连接的广播,后面会根据网络的链接是否重发请求:

private static class RequestManagerConnectivityListener implements ConnectivityMonitor.ConnectivityListener {
    private final RequestTracker requestTracker;

    public RequestManagerConnectivityListener(RequestTracker requestTracker) {
        this.requestTracker = requestTracker;
    }

    @Override
    public void onConnectivityChanged(boolean isConnected) {
        if (isConnected) {
            requestTracker.restartRequests();
        }
    }
}

       上述代码就是当网络从断开到链接变化时,重发请求。

设置请求参数

       前面解析了RequestManager的构造过程,之后我们load了url,设置缩放类型,加入了缓存策略,设置了图片裁剪方式,设置了占位图与加载失败后的错误图,这里我们看看load url的过程。

public DrawableTypeRequest<String> load(String string) {
    return (DrawableTypeRequest<String>) fromString().load(string);
}


public DrawableTypeRequest<String> fromString() {
    return loadGeneric(String.class);
}

private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
    ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
    ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
            Glide.buildFileDescriptorModelLoader(modelClass, context);
    if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
        throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
                + " which there is a registered ModelLoader, if you are using a custom model, you must first call"
                + " Glide#register with a ModelLoaderFactory for your custom model class");
    }

    return optionsApplier.apply(
            new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
                    glide, requestTracker, lifecycle, optionsApplier));
}

       首先调用了load参数为string,返回一个DrawableTypeRequest, 这里注意参数类型,之后接着调用了fromString的load,而fromString又调用了loadGeneric, 我们来看看loadGeneric的过程:

1.Glide.buildStreamModelLoader(modelClass, context);
首先根据modelClass构造了streamModelLoader,这里的modelClass类型为String,之后我们会看看构造过程
2. Glide.buildFileDescriptorModelLoader(modelClass, context)
根据modelClass构造了fileDescriptorModelLoader,这里的modelClass类型为String,
根据上面的构造的两种loader创建DrawableTypeRequest,
3.optionsApplier为RequestManager中构造的,但是这里它里面调用的options为空,因此不做任何操作。

       我们继续看看buildStreamModelLoader,他里面继续调用了如下的函数:

public static <T> ModelLoader<T, InputStream> buildStreamModelLoader(Class<T> modelClass, Context context) {
    return buildModelLoader(modelClass, InputStream.class, context);
}

public static <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass,
        Context context) {
     if (modelClass == null) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Unable to load null model, setting placeholder only");
        }
        return null;
    }
    return Glide.get(context).getLoaderFactory().buildModelLoader(modelClass, resourceClass);
}

       这里继续调用了三个参数的buildModelLoader, modelClass为String, ResourceClass为InputStream,context还是外部传过来的上下文。里面继续调用了Glide.get(context).getLoaderFactory().buildModelLoader(),这里首先获取Glide的实例,Glide是一个单例这里要单独说说他们的实例话过程,后续会用到Glide实例的参数。等Glide分析后再回来解析DrawableTypeRequest的创建过程。

Glide初始化

       Glide.get(context)首先实例化:

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

       这里实例化双重检查,因为我们前面已经看到了可能会多线程加载,ui线程,后台线程同时加载,双重检查构造单例。首先解析AndroidManifest中配置的Meta_data节点,key为GlideModule,这里我们没有配置。之后构造一个GlideBuilder,主要是设置了context,在之后调用createGlide生成一个Glide实例。createGlide如下:

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

       主要是设置各种参数:

  • sourceService,首先设置一个先进先出的ThreadPoolExecutor
  • diskCacheService, 磁盘缓存先进先出的Executor
  • bitmapPool, bitmap缓存,现在一般都是api11以上,因此是LruBitmapPool
  • memoryCache 内存缓存
  • diskCacheFactory, 磁盘缓存工厂
  • engine,加载管理缓存引擎
  • decodeFormat,解码格式,默认RGB_565,占用两个字节

       最后根据上诉的参数调用Glide的构造函数:

Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
    this.engine = engine;
    this.bitmapPool = bitmapPool;
    this.memoryCache = memoryCache;
    this.decodeFormat = decodeFormat;
    loaderFactory = new GenericLoaderFactory(context);
    mainHandler = new Handler(Looper.getMainLooper());
    bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);

    dataLoadProviderRegistry = new DataLoadProviderRegistry();

    StreamBitmapDataLoadProvider streamBitmapLoadProvider =
            new StreamBitmapDataLoadProvider(bitmapPool, decodeFormat);
    dataLoadProviderRegistry.register(InputStream.class, Bitmap.class, streamBitmapLoadProvider);

    FileDescriptorBitmapDataLoadProvider fileDescriptorLoadProvider =
            new FileDescriptorBitmapDataLoadProvider(bitmapPool, decodeFormat);
    dataLoadProviderRegistry.register(ParcelFileDescriptor.class, Bitmap.class, fileDescriptorLoadProvider);

    ImageVideoDataLoadProvider imageVideoDataLoadProvider =
            new ImageVideoDataLoadProvider(streamBitmapLoadProvider, fileDescriptorLoadProvider);
    dataLoadProviderRegistry.register(ImageVideoWrapper.class, Bitmap.class, imageVideoDataLoadProvider);

    GifDrawableLoadProvider gifDrawableLoadProvider =
            new GifDrawableLoadProvider(context, bitmapPool);
    dataLoadProviderRegistry.register(InputStream.class, GifDrawable.class, gifDrawableLoadProvider);

    dataLoadProviderRegistry.register(ImageVideoWrapper.class, GifBitmapWrapper.class,
            new ImageVideoGifDrawableLoadProvider(imageVideoDataLoadProvider, gifDrawableLoadProvider, bitmapPool));

    dataLoadProviderRegistry.register(InputStream.class, File.class, new StreamFileDataLoadProvider());

    register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
    register(File.class, InputStream.class, new StreamFileLoader.Factory());
    register(int.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
    register(int.class, InputStream.class, new StreamResourceLoader.Factory());
    register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
    register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());
    register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());
    register(String.class, InputStream.class, new StreamStringLoader.Factory());
    register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());
    register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
    register(URL.class, InputStream.class, new StreamUrlLoader.Factory());
    register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
    register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());

    transcoderRegistry.register(Bitmap.class, GlideBitmapDrawable.class,
            new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool));
    transcoderRegistry.register(GifBitmapWrapper.class, GlideDrawable.class,
            new GifBitmapWrapperDrawableTranscoder(
                    new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool)));

    bitmapCenterCrop = new CenterCrop(bitmapPool);
    drawableCenterCrop = new GifBitmapWrapperTransformation(bitmapPool, bitmapCenterCrop);

    bitmapFitCenter = new FitCenter(bitmapPool);
    drawableFitCenter = new GifBitmapWrapperTransformation(bitmapPool, bitmapFitCenter);
}

这里设置的参数后面都会用到,可以进行反查

       可以看到不仅设置了众多的参数,还register了众多的ModelLoaderFactory,注册过程就不详细解析了,只是为了说明初始化了这些参数,现在我们继续回到之前的Glide.buildStreamModelLoader(modelClass, context)。

buildStreamModelLoader

       上面中断的过程主要是因为buildStreamModelLoader用到了Glide初始化中的参数,这里我们继续往下看。他先调用了getLoaderFactory()获得loaderFactory,loaderFactory是在Glide初始化创建的,并且注册了众多的ModelLoaderFactory。之后调用了buildModelLoader生成一个ModelLoader:

public synchronized <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass) {
    ModelLoader<T, Y> result = getCachedLoader(modelClass, resourceClass);
    if (result != null) {
        // We've already tried to create a model loader and can't with the currently registered set of factories,
        // but we can't use null to demonstrate that failure because model loaders that haven't been requested
        // yet will be null in the cache. To avoid this, we use a special signal model loader.
        if (NULL_MODEL_LOADER.equals(result)) {
            return null;
        } else {
            return result;
        }
    }

    final ModelLoaderFactory<T, Y> factory = getFactory(modelClass, resourceClass);
    if (factory != null) {
        result = factory.build(context, this);
        cacheModelLoader(modelClass, resourceClass, result);
    } else {
        // We can't generate a model loader for the given arguments with the currently registered set of factories.
        cacheNullLoader(modelClass, resourceClass);
    }
    return result;
}

       这里modelClass为String,resourceClass为InputStream,首先在cache中获取,第一次调用cache中是没有的,因此会调用getFactory(modelClass, resourceClass)获得ModelLoaderFactory,会根据modelClass, resourceClass来获取对应的ModelLoaderFactory,我们去Glide的构造函数中看看注册为String与InputStream的Factory是什么?

register(String.class, InputStream.class, new StreamStringLoader.Factory()); 

       因此这里的ModelLoaderFactory为StreamStringLoader.Factory(), 因此我们看看他的Build函数:

public static class Factory implements ModelLoaderFactory<String, InputStream> {
    @Override
    public ModelLoader<String, InputStream> build(Context context, GenericLoaderFactory factories) {
        return new StreamStringLoader(factories.buildModelLoader(Uri.class, InputStream.class));
    }

    @Override
    public void teardown() {
        // Do nothing.
    }
}

       这里又继续调用了factories.buildModelLoader(Uri.class, InputStream.class),只不过第一个参数已经转变成了Uri类型,因此我们查查注册为Uri与InputStream的ModelLoaderFactory,因此又会调用到StreamUriLoader.Factory()的build函数,这里又级联调用了factories.buildModelLoader(GlideUrl.class, InputStream.class),因此最终又调用到了HttpUrlGlideUrlLoader.Factory(),最终返回了HttpUrlGlideUrlLoader。

buildFileDescriptorModelLoader

       buildFileDescriptorModelLoader的整个流程与buildStreamModelLoader类似,因此这个就不展开了。

DrawableTypeRequest

       构造了streamModelLoader与fileDescriptorModelLoader后根据这些参数构造了DrawableTypeRequest,DrawableTypeRequest首先调用了DrawableRequestBuilder的构造函数:

DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
        ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
        RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
    super(context, modelClass,
            buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
                    GlideDrawable.class, null),
            glide, requestTracker, lifecycle);
    this.streamModelLoader = streamModelLoader;
    this.fileDescriptorModelLoader = fileDescriptorModelLoader;
    this.optionsApplier = optionsApplier;
}

       这里有一个buildProvider的过程,根据参数创建了FixedLoadProvider。我们又回到最初设置参数的地方,load url,前面只是解析了fromString,还没有设置最终的url参数,最后根据DrawableTypeRequest设置各种参数,比如url,设置bitmapTransform参数,各种参数就不展开了。

加载过程

       我们的调用样例中最后一步是into(view),我们可以看到里面调用了super.into(view)也就是GenericRequestBuilder的into函数,代码如下:

public Target<TranscodeType> into(ImageView view) {
    Util.assertMainThread();
    if (view == null) {
        throw new IllegalArgumentException("You must pass in a non null View");
    }

    if (!isTransformationSet && view.getScaleType() != null) {
        switch (view.getScaleType()) {
            case CENTER_CROP:
                applyCenterCrop();
                break;
            case FIT_CENTER:
            case FIT_START:
            case FIT_END:
                applyFitCenter();
                break;
            //$CASES-OMITTED$
            default:
                // Do nothing.
        }
    }

    return into(glide.buildImageViewTarget(view, transcodeClass));
}

       首先判断view是否为空,如果为空,抛出异常,之后判断是否设置了ScaleType,根据设置的效果来处理。最后继续调用into函数,不过先将View重新build成ViewTarget,调用:

<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}

// 
public class ImageViewTargetFactory {

    @SuppressWarnings("unchecked")
    public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
        if (GlideDrawable.class.isAssignableFrom(clazz)) {
            return (Target<Z>) new GlideDrawableImageViewTarget(view);
        } else if (Bitmap.class.equals(clazz)) {
            return (Target<Z>) new BitmapImageViewTarget(view);
        } else if (Drawable.class.isAssignableFrom(clazz)) {
            return (Target<Z>) new DrawableImageViewTarget(view);
        } else {
            throw new IllegalArgumentException("Unhandled class: " + clazz
                    + ", try .as*(Class).transcode(ResourceTranscoder)");
        }
    }
}

       这里imageView就是传入的view,transcodedClass的值又是什么呐? 前面我们得到了DrawableTypeRequest,他的父类为DrawableRequestBuilder,DrawableRequestBuilder的父类为GenericRequestBuilder,我们在DrawableTypeRequest的构造函数中调用了DrawableRequestBuilder,这里我们传入了transcodedClass,为GlideDrawable.class,因此最终这里的返回的是GlideDrawableImageViewTarget,我们继续看into函数。

public <Y extends Target<TranscodeType>> Y into(Y target) {
    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线程,androd中各种控件的展示都是在UI线程操作的,之后判断target是否为空,为空抛出异常,之后获取上一次request,这里第一次调用previous为空,之后调用buildRequest构造一个request,buildRequest首先设置了优先级,之后继续调用buildRequestRecursive这里就不贴代码了,先判断缩略请求是否为空,多尺寸缩略是否为空,否则就调用obtainRequest获的request,obtainRequest里面又继续调用GenericRequest.obtain获得request,obtain中的参数就是之前构造时候设置的参数。这里就不展开了。

       之后将request设置到target,lifecycle添加一个listener,最后requestTracker调用runRequest开始执行该request:

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

       首先将request加入到请求队列中,如果当前页面不是paused状态,则开始调用request的begin函数,否则加入pending队列,当状态变为resume时,继续执行,我们继续看看begin函数:

@Override
public void begin() {
    startTime = LogTime.getLogTime();
    if (model == null) {
        onException(null);
        return;
    }

    status = Status.WAITING_FOR_SIZE;
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        onSizeReady(overrideWidth, overrideHeight);
    } else {
        target.getSize(this);
    }

    if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
        target.onLoadStarted(getPlaceholderDrawable());
    }
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logV("finished run method in " + LogTime.getElapsedMillis(startTime));
    }
}

       这里实际调用的是GenericRequest的begin函数,首先记录开始时间,之后设置status为WAITING_FOR_SIZE,等待获取图片的尺寸,如果当前宽高已经获取到了,调用onSizeReady,否则调用getSize,getSize在尺寸确定后会调用回调SizeReadyCallback的onSizeReady函数,之后如果还没有加载完成活失败,则先显示占位图。

注意展位图只能是resource资源

       我们继续看看他是怎么获得图片的尺寸的,getSize会调用父类的getSize,这里是一个接口,实际调用的是ViewTarget的getSize,为什么是ViewTarget,还记得我们之前buildTarget过程吧!这里又继续调用了 sizeDeterminer.getSize(cb):

public void getSize(SizeReadyCallback cb) {
    int currentWidth = getViewWidthOrParam();
    int currentHeight = getViewHeightOrParam();
    if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
        cb.onSizeReady(currentWidth, currentHeight);
    } else {
        // We want to notify callbacks in the order they were added and we only expect one or two callbacks to
        // be added a time, so a List is a reasonable choice.
        if (!cbs.contains(cb)) {
            cbs.add(cb);
        }
        if (layoutListener == null) {
            final ViewTreeObserver observer = view.getViewTreeObserver();
            layoutListener = new SizeDeterminerLayoutListener(this);
            observer.addOnPreDrawListener(layoutListener);
        }
    }
}

       这里add系统OnPreDrawListener回调,根据系统回调来确定的宽高。

onSizeReady

       我们来着重看看onSizeReady函数:

public void onSizeReady(int width, int height) {
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
    if (status != Status.WAITING_FOR_SIZE) {
        return;
    }
    status = Status.RUNNING;

    width = Math.round(sizeMultiplier * width);
    height = Math.round(sizeMultiplier * height);

    ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
    final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);

    if (dataFetcher == null) {
        onException(new Exception("Failed to load model: \'" + model + "\'"));
        return;
    }
    ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
    }
    loadedFromMemoryCache = true;
    loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
            priority, isMemoryCacheable, diskCacheStrategy, this);
    loadedFromMemoryCache = resource != null;
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
}

       先判断状态是不是等待获取尺寸,如果不是表示已经执行过了,之后将状态设置Running状态,之后重新计算宽高,不过这里sizeMultiplier为1,尺寸其实不变,之后获取ModelLoader,ModelLoader我们在上面已经分析过了。

  • 首先是StreamStringLoader
  • 其次是StreamUriLoader
  • 最后是HttpUrlGlideUrlLoader

       之后调用modelLoader.getResourceFetcher(model, width, height),那这里首先调用的StreamStringLoader的getResourceFetcher,可以发现他根本没有getResourceFetcher这个函数,他实际调用的是StringLoader的getResourceFetcher:

@Override
public DataFetcher<T> getResourceFetcher(String model, int width, int height) {
    Uri uri;
    if (TextUtils.isEmpty(model)) {
        return null;
    } else if (model.startsWith("/")) {
        uri = toFileUri(model);
    } else {
        uri = Uri.parse(model);
        final String scheme = uri.getScheme();
        if (scheme == null) {
            uri = toFileUri(model);
        }
    }

    return uriLoader.getResourceFetcher(uri, width, height);
}

       modle就是传入的url,如果url为空直接返回空,如果传入串是’/’开头的,表示从本地读取,会在串的前面加入“file”标识,否则转换为Uri,获取Scheme,接着调用uriLoader.getResourceFetcher(uri, width, height),这里的uriLoader为StreamUriLoader,因此调用的是StreamUriLoader的getResourceFetcher,最终调用的是UriLoader的getResourceFetcher:

@Override
public final DataFetcher<T> getResourceFetcher(Uri model, int width, int height) {
    final String scheme = model.getScheme();

    DataFetcher<T> result = null;
    if (isLocalUri(scheme)) {
        if (AssetUriParser.isAssetUri(model)) {
            String path = AssetUriParser.toAssetPath(model);
            result = getAssetPathFetcher(context, path);
        } else {
            result = getLocalUriFetcher(context, model);
        }
    } else if (urlLoader != null && ("http".equals(scheme) || "https".equals(scheme))) {
        result = urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height);
    }

    return result;
}

       如果scheme为file,content,android:resource都表示从本地获取,否则如果urlLoader不为空,切scheme为http或者https,则调用urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height),这里的urlLoader为HttpUrlGlideUrlLoader,因此最终调用的是HttpUrlGlideUrlLoader的getResourceFetcher:

@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
    // 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;
        }
    }
    return new HttpUrlFetcher(url);
}

       前扰万绕终于到HttpUrlFetcher了,之后获取transcoder,transcoder是前面我们构造DrawableTypeRequest生成的,这里resourceClass为GifBitmapWrapper,transcodedClass为GlideDrawable,获取出来为空,之后调用engine.load函数,参数为之前构造的参数:

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

    。。。。

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

       这里代码太长,将其他代码给注释了,我们主要看会构建一个engineJob,之后构建一个decodeJob,构建一个EngineRunnable,之后调用addCallback设置回调ResourceCallback,最后调用start将Runnable submit,最终会回到EngineRunnable的run函数:

@Override
public void run() {
    if (isCancelled) {
        return;
    }

    Exception exception = null;
    Resource<?> resource = null;
    try {
        resource = decode();
    } catch (Exception e) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "Exception decoding", e);
        }
        exception = e;
    }

    if (isCancelled) {
        if (resource != null) {
            resource.recycle();
        }
        return;
    }

    if (resource == null) {
        onLoadFailed(exception);
    } else {
        onLoadComplete(resource);
    }
}

       我们继续跟踪decode函数,获取图片资源,获取成功后会通知ResourceCallback的onResourceReady,同时view显示,我们先来看看decode函数,会先判断是否从缓存中获取,否则调用decodeFromSource(),最终调用DecodeJob的decodeFromSource():

public Resource<Z> decodeFromSource() throws Exception {
    Resource<T> decoded = decodeSource();
    return transformEncodeAndTranscode(decoded);
}

       先decodeSource,在对resource进程变换,decodeSource如下:

private Resource<T> decodeSource() throws Exception {
    Resource<T> decoded = null;
    try {
        long startTime = LogTime.getLogTime();
        final A data = fetcher.loadData(priority);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Fetched data", startTime);
        }
        if (isCancelled) {
            return null;
        }
        decoded = decodeFromSourceData(data);
    } finally {
        fetcher.cleanup();
    }
    return decoded;
}

       终于又看到了fetcher了,这里fetcher就是前面生成的HttpUrlFetcher,因此调用的是HttpUrlFetcher的loadData,这里主要是网络操作了获取一个InputStream流。之后对获取的InputStream进行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);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Decoded from source", startTime);
        }
    }
    return decoded;
}

       先判断是否cache,则先缓存data,这里我们设置的需要缓存,则会调用cacheAndDecodeSourceData:

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);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Wrote source to cache", startTime);
    }

    startTime = LogTime.getLogTime();
    Resource<T> result = loadFromCache(resultKey.getOriginalKey());
    if (Log.isLoggable(TAG, Log.VERBOSE) && result != null) {
        logWithTimeAndKey("Decoded source from cache", startTime);
    }
    return result;
}

       先构造一个SourceWriter,参数是loadProvider.getSourceEncoder()与data,这里第一个参数是我们之前构造DrawableTypeRequest生成的,也就是ImageVideoGifDrawableLoadProvider的getSourceEncoder,主要是解析是否是gif图片,并写入缓存,之后返回写入的文件。

       这些都执行完成后调用transformEncodeAndTranscode,这里主要代用我们传入的Transformation对资源进行转换,比如裁剪成圆形,或者带有圆角的等:

private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
    long startTime = LogTime.getLogTime();
    Resource<T> transformed = transform(decoded);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Transformed resource from source", startTime);
    }

    writeTransformedToCache(transformed);

    startTime = LogTime.getLogTime();
    Resource<Z> result = transcode(transformed);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Transcoded transformed from source", startTime);
    }
    return result;
}

       这先调用transform对资源进程转换,之后在调用transcode对资源进行转换,这些都是之前我们设置的参数进行操作的。

       整个过程完成后,会调用onLoadComplete,主要是通知callback告诉Resource已经准备好了,最终会回到GenericRequest,该GenericRequest就是我们之前创建的Request,最终会调用GenericRequest的onResourceReady:

private void onResourceReady(Resource<?> resource, R result) {
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;

    if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
            isFirstResource)) {
        GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
        target.onResourceReady(result, animation);
    }

    notifyLoadSuccess();

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: "
                + (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache);
    }
}

       这里最终会调用我们之前创建的Target,这里的target就是前面build创建的GlideDrawableImageViewTarget,因此会调用GlideDrawableImageViewTarget的onResourceReady:

@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
    if (!resource.isAnimated()) {
        //TODO: Try to generalize this to other sizes/shapes.
        // This is a dirty hack that tries to make loading square thumbnails and then square full images less costly
        // by forcing both the smaller thumb and the larger version to have exactly the same intrinsic dimensions.
        // If a drawable is replaced in an ImageView by another drawable with different intrinsic dimensions,
        // the ImageView requests a layout. Scrolling rapidly while replacing thumbs with larger images triggers
        // lots of these calls and causes significant amounts of jank.
        float viewRatio = view.getWidth() / (float) view.getHeight();
        float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight();
        if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN
                && Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) {
            resource = new SquaringDrawable(resource, view.getWidth());
        }
    }
    super.onResourceReady(resource, animation);
    this.resource = resource;
    resource.setLoopCount(maxLoopCount);
    resource.start();
}

       这里先判断该图片是否是动画类型,也就是gif类型,这个是根据之前decode解码出来的,那一部分没有详细解析,如果是这设置动画的执行次数。

结束语

       到此一次执行的大致流程就结束了,其中还有很多细小的枝节没有详细分析,不过可以根据流程看一遍。

       诸如其他比如从本地加载,或者从assert加载流程都是一样的。

       目前Glide对本地resource是不能进行transform的,因此如果有需要可以对他进行改造,不过Glide官方也说了会从4.0开始支持。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章