Glide3.7.0源码详解

2023-11-03

基于的Glide版本:3.7.0

本文分析的是Glide最基本的一行代码:

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

我们认准这一个功能点,来看看Glide默默为我们做了什么。这篇文章会分析这行代码背后的代码,也会简单地分析代码用到的框架和设计模式。

这行代码可很容易分成三个部分:with、load、into,我们也降分为三个分类来分析。首先来看with的代码。

一、 with方法

with方法有很多重载方法,可以传入context、android.app.Activity、android.app.Fragment、v4.app.FragmentActivity、v4.app.Fragment。

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

不论哪个重载方法,都会先通过RequestManagerRetriever.get()获取RequestManagerRetriever对象,这个get()同with一样,也有很多重载方法,可传入的对象与with()相同。

    private RequestManager getApplicationManager(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.
                    applicationManager = new RequestManager(context.getApplicationContext(),
                            new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
                }
            }
        }

        return applicationManager;
    }

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

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    private static void assertNotDestroyed(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
            throw new IllegalArgumentException("You cannot start a load for a destroyed activity");
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    public RequestManager get(android.app.Fragment fragment) {
        if (fragment.getActivity() == null) {
            throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
        }
        if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            return get(fragment.getActivity().getApplicationContext());
        } else {
            android.app.FragmentManager fm = fragment.getChildFragmentManager();
            return fragmentGet(fragment.getActivity(), fm);
        }
    }

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

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

    SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) {
        SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(


            FRAGMENT_TAG);
        if (current == null) {
            current = pendingSupportRequestManagerFragments.get(fm);
            if (current == null) {
                current = new SupportRequestManagerFragment();
                pendingSupportRequestManagerFragments.put(fm, current);
                fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
                handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
            }
        }
        return current;
    }

    RequestManager supportFragmentGet(Context context, FragmentManager fm) {
        SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }

RequestManagerRetriever的get方法,最后有两种情况:

1. 非ApplicationContext,转换为ApplicationContext,通过double check的方式创建RequestManager的单例并返回。

2. 创建一个没有View的Fragment,在Fragment中创建RequestManager对象并返回。

第二种情况里,没有View的Fragment是为了监听Activity或Fragment的生命周期,当Activity或Fragment被销毁时,Glide的加载也就没有必要了。

with()方法总结:返回RequestManager对象。

使用到的设计模式:懒汉式加载double check单例模式。

二、 load方法

从load方法开始,就开始复杂了些。上面已经说过,with方法返回的是一个RequestManager对象,那么load方法就在RequestManager类中。

load方法也有很多重载,可以传入String、int、Integer、Uri、URL、File、byte数组、泛型。不论哪种重载的方法,最后都会走到loadGeneric(Class<T> modelClass)中,再调用loadGeneric方法返回值的load方法。

    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类型,这里也以String类型为例子进行分析。

loadGeneric方法的传入类型为String.class,注意看streamModelLoader,Glide首先创建了一个ModelLoader,这个ModelLoader贯穿了整个加载方法的始终。因为比较重要,这里我们对steamModelLoader的实现类进行分析,读者也可以直接看分析结果,跳过实现类的分析过程。

具体实现类分析1:

可以看到ModelLoader类是一个接口类,通过Glide类的buildStreamModelLoader(Class<T> modelClass, Context context),第一个参数modelClass是String.class,context就是我们通过with()方法传入的context。

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

buildStreamModelLoader方法又调用了buildModelLoader方法,并传入了InputStream.class参数。

    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方法先获取到Glide初始化方法里预加载的工厂类,再通过工厂类创建ModelLoader对象。

    private GenericLoaderFactory getLoaderFactory() {
        return loaderFactory;
    }

获取工厂的方法直接返回了loaderFactory对象,这个loaderFactory对象是在Glide类的构造函数中创建的。再来看看工厂类的buildModelLoader方法。

public class GenericLoaderFactory {
    private final Map<Class/*T*/, Map<Class/*Y*/, ModelLoaderFactory/*T, Y*/>> modelClassToResourceFactories =
            new HashMap<Class, Map<Class, ModelLoaderFactory>>();
    private final Map<Class/*T*/, Map<Class/*Y*/, ModelLoader/*T, Y*/>> cachedModelLoaders =
            new HashMap<Class, Map<Class, ModelLoader>>();
    private final Context context;

    public GenericLoaderFactory(Context context) {
       this.context = context.getApplicationContext();
    }

    public synchronized <T, Y> ModelLoaderFactory<T, Y> register(Class<T> modelClass, Class<Y> resourceClass,
            ModelLoaderFactory<T, Y> factory) {
        cachedModelLoaders.clear();

        Map<Class/*Y*/, ModelLoaderFactory/*T, Y*/> resourceToFactories = modelClassToResourceFactories.get(modelClass);
        if (resourceToFactories == null) {
            resourceToFactories = new HashMap<Class/*Y*/, ModelLoaderFactory/*T, Y*/>();
            modelClassToResourceFactories.put(modelClass, resourceToFactories);
        }

        ModelLoaderFactory/*T, Y*/ previous = resourceToFactories.put(resourceClass, factory);

        if (previous != null) {
            // This factory may be being used by another model. We don't want to say it has been removed unless we
            // know it has been removed for all models.
            for (Map<Class/*Y*/, ModelLoaderFactory/*T, Y*/> factories : modelClassToResourceFactories.values()) {
                if (factories.containsValue(previous)) {
                    previous = null;
                    break;
                }
            }
        }

        return previous;
    }

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

    private <T, Y> void cacheModelLoader(Class<T> modelClass, Class<Y> resourceClass, ModelLoader<T, Y> modelLoader) {
        Map<Class/*Y*/, ModelLoader/*T, Y*/> resourceToLoaders = cachedModelLoaders.get(modelClass);
        if (resourceToLoaders == null) {
            resourceToLoaders = new HashMap<Class/*Y*/, ModelLoader/*T, Y*/>();
            cachedModelLoaders.put(modelClass, resourceToLoaders);
        }
        resourceToLoaders.put(resourceClass, modelLoader);
    }

    private <T, Y> ModelLoader<T, Y> getCachedLoader(Class<T> modelClass, Class<Y> resourceClass) {
        Map<Class/*Y*/, ModelLoader/*T, Y*/> resourceToLoaders = cachedModelLoaders.get(modelClass);
        ModelLoader/*T, Y*/ result = null;
        if (resourceToLoaders != null) {
            result = resourceToLoaders.get(resourceClass);
        }

        return result;
    }

    private <T, Y> ModelLoaderFactory<T, Y> getFactory(Class<T> modelClass, Class<Y> resourceClass) {
        Map<Class/*Y*/, ModelLoaderFactory/*T, Y*/> resourceToFactories = modelClassToResourceFactories.get(modelClass);
        ModelLoaderFactory/*T, Y*/ result = null;
        if (resourceToFactories != null) {
            result = resourceToFactories.get(resourceClass);
        }

        if (result == null) {
            for (Class<? super T> registeredModelClass : modelClassToResourceFactories.keySet()) {
                // This accounts for model subclasses, our map only works for exact matches. We should however still
                // match a subclass of a model with a factory for a super class of that model if if there isn't a
                // factory for that particular subclass. Uris are a great example of when this happens, most uris
                // are actually subclasses for Uri, but we'd generally rather load them all with the same factory rather
                // than trying to register for each subclass individually.
                if (registeredModelClass.isAssignableFrom(modelClass)) {
                    Map<Class/*Y*/, ModelLoaderFactory/*T, Y*/> currentResourceToFactories =
                            modelClassToResourceFactories.get(registeredModelClass);
                    if (currentResourceToFactories != null) {
                        result = currentResourceToFactories.get(resourceClass);
                        if (result != null) {
                            break;
                        }
                    }
                }
            }
        }

        return result;
    }
}

先来看buildModelLoader方法,入参是String.class和InputStream.class,第一句话就是getCachedLoader方法,获取缓存的ModelLoader对象,这里有没有预先缓存ModelLoader呢。从getCachedLoader方法中可以看出是从预定义的Map对象cachedModelLoaders中获取缓存,可以看到cachedModelLoaders是private final类型,类中没有提供外部的操作可操作方法,类中有cacheModelLoader方法缓存创建完成的ModelLoader对象。可以看出第一次运行到这里是没有预缓存的。

回到buildModelLoader方法,既然没有预缓存,getCachedLoader返回的result自然是null,往下就走到了getFactory方法。看一下getFactory的具体实现。和getCachedLoader方法类似,从预定义的Map对象modelClassToResourceFactories中读取缓存。那么这个modelClassToResourceFactories有没有预缓存工厂方法在里面呢。答案是有的,我们来看Glide的构造器。

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

    ...

    public <T, Y> void register(Class<T> modelClass, Class<Y> resourceClass, ModelLoaderFactory<T, Y> factory) {
        ModelLoaderFactory<T, Y> removed = loaderFactory.register(modelClass, resourceClass, factory);
        if (removed != null) {
            removed.teardown();
        }
    }

    ...

}

注意Glide构造器中的register方法,构造器中预缓存了很多个工厂方法,构造器的register方法中,又调用了工厂类的register方法,将预加载的工厂方法,缓存到modelClassToResourceFactories中。

再回到工厂方法类(GenericLoaderFactory)的buildModelLoader中,从register和getFactory方法可以看出,getFactory返回的是

String.class和InputStream.class对应的工厂方法,从Glide构造器中找出,它是StreamStringLoader.Factory()。

再来看StreamStringLoader.Factory()的实现:

public class StreamStringLoader extends StringLoader<InputStream> implements StreamModelLoader<String> {

    /**
     * The default factory for {@link com.bumptech.glide.load.model.stream.StreamStringLoader}s.
     */
    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.
        }
    }

    public StreamStringLoader(Context context) {
        this(Glide.buildStreamModelLoader(Uri.class, context));
    }

    public StreamStringLoader(ModelLoader<Uri, InputStream> uriLoader) {
        super(uriLoader);
    }
}

build方法里直接返回了Uri.class和InputStream.class对应的工厂方法。Uri.class和InputStream.class对应工厂方法从Glide构造器中可以看出,对应的是StreamUriLoader.Factory(),再看StreamUriLoader.Factory()方法会发现,它又直接返回了GlideUrl.class和InputStream.class对应的工厂方法HttpUrlGlideUrlLoader.Factory()。

public class HttpUrlGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {

    private final ModelCache<GlideUrl, GlideUrl> modelCache;

    /**
     * The default factory for {@link com.bumptech.glide.load.model.stream.HttpUrlGlideUrlLoader}s.
     */
    public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
        private final ModelCache<GlideUrl, GlideUrl> modelCache = new ModelCache<GlideUrl, GlideUrl>(500);

        @Override
        public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new HttpUrlGlideUrlLoader(modelCache);
        }

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

    public HttpUrlGlideUrlLoader() {
        this(null);
    }

    public HttpUrlGlideUrlLoader(ModelCache<GlideUrl, GlideUrl> modelCache) {
        this.modelCache = modelCache;
    }

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

HttpUrlGlideUrlLoader.Factory()的build方法,返回了当前的ModelLoader对象HttpUrlGlideUrlLoader。这个才是loadGeneric方法中创建的streamModelLoader的具体实现类。

具体实现类分析1结论:steamModelLoader的实现类是HttpUrlGlideUrlLoader。

用到的设计模式:工厂方法设计模式。(一个工厂方法,建立一个对应的ModelLoader)

 

继续来看loadGeneric,创建完streamModelLoader后,又创建了fileDescriptorModelLoader,这个ModelLoader的创建和streamModelLoader的创建相似。

loadGeneric最后创建并返回了一个DrawableTypeRequest对象,并把刚才创建的streamModelLoader、fileDescriptorModelLoader传入了DrawableTypeRequest的构造器函数。

loadGeneric将DrawableTypeRequest对象返回给fromString,fromString再将DrawableTypeRequest对象返回给load(String string),再在RequestManager的load(String string)方法中调用DrawableTypeRequest对象的load(String string)函数。

DrawableTypeRequest对象的load方法其实在DrawableTypeRequest的父类DrawableRequestBuilder中。DrawableRequestBuilder又继承自GenericRequestBuilder。

继承关系:DrawableTypeRequest   ->   DrawableRequestBuilder   ->   GenericRequestBuilder

这三个对象包含了很多重要对象的初始化,以及设置加载方式的方法。对这三个重要的类,我们逐一分析。

1. DrawableTypeRequest 

public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions {
   
    ...

    /**
     * Attempts to always load the resource as a {@link android.graphics.Bitmap}, even if it could actually be animated.
     *
     * @return A new request builder for loading a {@link android.graphics.Bitmap}
     */
    public BitmapTypeRequest<ModelType> asBitmap() {
        return optionsApplier.apply(new BitmapTypeRequest<ModelType>(this, streamModelLoader,
                fileDescriptorModelLoader, optionsApplier));
    }

    /**
     * Attempts to always load the resource as a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
     * <p>
     *     If the underlying data is not a GIF, this will fail. As a result, this should only be used if the model
     *     represents an animated GIF and the caller wants to interact with the GIfDrawable directly. Normally using
     *     just an {@link com.bumptech.glide.DrawableTypeRequest} is sufficient because it will determine whether or
     *     not the given data represents an animated GIF and return the appropriate animated or not animated
     *     {@link android.graphics.drawable.Drawable} automatically.
     * </p>
     *
     * @return A new request builder for loading a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
     */
    public GifTypeRequest<ModelType> asGif() {
        return optionsApplier.apply(new GifTypeRequest<ModelType>(this, streamModelLoader, optionsApplier));
    }
    
    ...

}

先抛开构造函数,看看DrawableTypeRequest的其他方法,最显眼的两个方法是asBitmap()和asGif(),同时还有downloadOnly方法。

2. DrawableRequestBuilder

看类名可以知道,这里使用了Builder构造器模式,通过构造器模式实现链接使用。

public class DrawableRequestBuilder<ModelType>
        extends GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>
        implements BitmapOptions, DrawableOptions {

    ...    

    /**
     * Loads and displays the {@link GlideDrawable} retrieved by the given thumbnail request if it finishes before this
     * request. Best used for loading thumbnail {@link GlideDrawable}s that are smaller and will be loaded more quickly
     * than the fullsize {@link GlideDrawable}. There are no guarantees about the order in which the requests will
     * actually finish. However, if the thumb request completes after the full request, the thumb {@link GlideDrawable}
     * will never replace the full image.
     *
     * @see #thumbnail(float)
     *
     * <p>
     *     Note - Any options on the main request will not be passed on to the thumbnail request. For example, if
     *     you want an animation to occur when either the full {@link GlideDrawable} loads or the thumbnail loads,
     *     you need to call {@link #animate(int)} on both the thumb and the full request. For a simpler thumbnail
     *     option where these options are applied to the humbnail as well, see {@link #thumbnail(float)}.
     * </p>
     *
     * <p>
     *     Only the thumbnail call on the main request will be obeyed, recursive calls to this method are ignored.
     * </p>
     *
     * @param thumbnailRequest The request to use to load the thumbnail.
     * @return This builder object.
     */
    public DrawableRequestBuilder<ModelType> thumbnail(
            DrawableRequestBuilder<?> thumbnailRequest) {
        super.thumbnail(thumbnailRequest);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> thumbnail(
            GenericRequestBuilder<?, ?, ?, GlideDrawable> thumbnailRequest) {
        super.thumbnail(thumbnailRequest);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> thumbnail(float sizeMultiplier) {
        super.thumbnail(sizeMultiplier);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> sizeMultiplier(float sizeMultiplier) {
        super.sizeMultiplier(sizeMultiplier);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> decoder(ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> decoder) {
        super.decoder(decoder);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> cacheDecoder(ResourceDecoder<File, GifBitmapWrapper> cacheDecoder) {
        super.cacheDecoder(cacheDecoder);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> encoder(ResourceEncoder<GifBitmapWrapper> encoder) {
        super.encoder(encoder);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> priority(Priority priority) {
        super.priority(priority);
        return this;
    }

    /**
     * Transform {@link GlideDrawable}s using the given
     * {@link com.bumptech.glide.load.resource.bitmap.BitmapTransformation}s.
     *
     * <p>
     *     Note - Bitmap transformations will apply individually to each frame of animated GIF images and also to
     *     individual {@link Bitmap}s.
     * </p>
     *
     * @see #centerCrop()
     * @see #fitCenter()
     * @see #bitmapTransform(com.bumptech.glide.load.Transformation[])
     * @see #transform(com.bumptech.glide.load.Transformation[])
     *
     * @param transformations The transformations to apply in order.
     * @return This request builder.
     */
    public DrawableRequestBuilder<ModelType> transform(BitmapTransformation... transformations) {
        return bitmapTransform(transformations);
    }

    /**
     * Transform {@link GlideDrawable}s using {@link com.bumptech.glide.load.resource.bitmap.CenterCrop}.
     *
     * @see #fitCenter()
     * @see #transform(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
     * @see #bitmapTransform(com.bumptech.glide.load.Transformation[])
     * @see #transform(com.bumptech.glide.load.Transformation[])
     *
     * @return This request builder.
     */
    @SuppressWarnings("unchecked")
    public DrawableRequestBuilder<ModelType> centerCrop() {
        return transform(glide.getDrawableCenterCrop());
    }

    /**
     * Transform {@link GlideDrawable}s using {@link com.bumptech.glide.load.resource.bitmap.FitCenter}.
     *
     * @see #centerCrop()
     * @see #transform(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
     * @see #bitmapTransform(com.bumptech.glide.load.Transformation[])
     * @see #transform(com.bumptech.glide.load.Transformation[])
     *
     * @return This request builder.
     */
    @SuppressWarnings("unchecked")
    public DrawableRequestBuilder<ModelType> fitCenter() {
        return transform(glide.getDrawableFitCenter());
    }

    /**
     * Transform {@link GlideDrawable}s using the given {@link android.graphics.Bitmap} transformations. Replaces any
     * previous transformations.
     *
     * @see #fitCenter()
     * @see #centerCrop()
     * @see #transform(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
     * @see #transform(com.bumptech.glide.load.Transformation[])
     *
     * @return This request builder.
     */
    public DrawableRequestBuilder<ModelType> bitmapTransform(Transformation<Bitmap>... bitmapTransformations) {
        GifBitmapWrapperTransformation[] transformations =
                new GifBitmapWrapperTransformation[bitmapTransformations.length];
        for (int i = 0; i < bitmapTransformations.length; i++) {
            transformations[i] = new GifBitmapWrapperTransformation(glide.getBitmapPool(), bitmapTransformations[i]);
        }
        return transform(transformations);
    }



    /**
     * {@inheritDoc}
     *
     * @see #bitmapTransform(com.bumptech.glide.load.Transformation[])
     * @see #centerCrop()
     * @see #fitCenter()
     */
    @Override
    public DrawableRequestBuilder<ModelType> transform(Transformation<GifBitmapWrapper>... transformation) {
        super.transform(transformation);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> transcoder(
            ResourceTranscoder<GifBitmapWrapper, GlideDrawable> transcoder) {
        super.transcoder(transcoder);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public final DrawableRequestBuilder<ModelType> crossFade() {
        super.animate(new DrawableCrossFadeFactory<GlideDrawable>());
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public DrawableRequestBuilder<ModelType> crossFade(int duration) {
        super.animate(new DrawableCrossFadeFactory<GlideDrawable>(duration));
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Deprecated
    public DrawableRequestBuilder<ModelType> crossFade(Animation animation, int duration) {
        super.animate(new DrawableCrossFadeFactory<GlideDrawable>(animation, duration));
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public DrawableRequestBuilder<ModelType> crossFade(int animationId, int duration) {
        super.animate(new DrawableCrossFadeFactory<GlideDrawable>(context, animationId,
                duration));
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> dontAnimate() {
        super.dontAnimate();
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> animate(ViewPropertyAnimation.Animator animator) {
        super.animate(animator);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> animate(int animationId) {
        super.animate(animationId);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Deprecated
    @SuppressWarnings("deprecation")
    @Override
    public DrawableRequestBuilder<ModelType> animate(Animation animation) {
        super.animate(animation);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> placeholder(int resourceId) {
        super.placeholder(resourceId);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> placeholder(Drawable drawable) {
        super.placeholder(drawable);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> fallback(Drawable drawable) {
        super.fallback(drawable);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> fallback(int resourceId) {
        super.fallback(resourceId);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> error(int resourceId) {
        super.error(resourceId);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> error(Drawable drawable) {
        super.error(drawable);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> listener(
            RequestListener<? super ModelType, GlideDrawable> requestListener) {
        super.listener(requestListener);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> diskCacheStrategy(DiskCacheStrategy strategy) {
        super.diskCacheStrategy(strategy);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> skipMemoryCache(boolean skip) {
        super.skipMemoryCache(skip);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> override(int width, int height) {
        super.override(width, height);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> sourceEncoder(Encoder<ImageVideoWrapper> sourceEncoder) {
        super.sourceEncoder(sourceEncoder);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DrawableRequestBuilder<ModelType> dontTransform() {
        super.dontTransform();
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> signature(Key signature) {
        super.signature(signature);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> load(ModelType model) {
        super.load(model);
        return this;
    }

    @Override
    public DrawableRequestBuilder<ModelType> clone() {
        return (DrawableRequestBuilder<ModelType>) super.clone();
    }

    ...
}

这里类里,可以看到很多我们熟悉的方法,transform、centerCrop、fitCenter、animate、placeholder、error、listener、diskCacheStrategy、skipMemoryCache、dontTransform、signature,glide很多设置都在这里。

DrawableRequestBuilder的构造器模式也说明了asBitmap和asGif只能在load方法后调用,因为一旦使用了DrawableRequestBuilder中的方法,DrawableTypeRequest就向下转型变成了DrawableRequestBuilder,失去了asBitmap和asGif方法。

3. GenericRequestBuilder

同样是Builder构造器模式,DrawableRequestBuilder其实是GenericRequestBuilder的代理实现,更多的实现细节都在GenericRequestBuilder里。

 

看完了我们熟悉的代码,接下来要分析加载过程中的重要实现类,读者同样可以跳过分析过程看分析结果。

我们来看DrawableTypeRequest的构造器方法,很多重要的对象都在这里进行了初始化。

    private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
            ModelLoader<A, InputStream> streamModelLoader,
            ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
            Class<R> transcodedClass,
            ResourceTranscoder<Z, R> transcoder) {
        if (streamModelLoader == null && fileDescriptorModelLoader == null) {
            return null;
        }

        if (transcoder == null) {
            transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
        }
        DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
                resourceClass);
        ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
                fileDescriptorModelLoader);
        return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
    }

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

loadGeneric方法中,创建DrawableTypeRequest,并传入了很多东西。这里重点看buildProvider(),这个方法返回一个FixedLoadProvider对象,并传给了父类DrawableRequestBuilder的构造器。注意第17行,FixedLoadProvider对象中封装了三个对象,也是贯穿了整个加载过程。

FixedLoadProvider封装的第一个对象是ImageVideoModelLoader,这个比较简单,传入的是loadGeneric方法创建的streamModelLoader和fileDescriptorModelLoader。

FixedLoadProvider封装的第二个对象是ResourceTranscoder<Z, R> transcoder,我们来分析一下这个transcoder的具体实现。

具体类的实现分析2:

创建transcoder的glide.buildTranscoder()中,传入了两个class对象,分别是GifBitmapWrapper.calss和GlideDrawable.class。

看一下glide.buildTranscoder

    <Z, R> ResourceTranscoder<Z, R> buildTranscoder(Class<Z> decodedClass, Class<R> transcodedClass) {
        return transcoderRegistry.get(decodedClass, transcodedClass);
    }

直接调用了transcoderRegistry的get()方法,跟进去transcoderRegistry类中查看

public class TranscoderRegistry {
    private static final MultiClassKey GET_KEY = new MultiClassKey();

    private final Map<MultiClassKey, ResourceTranscoder<?, ?>> factories =
            new HashMap<MultiClassKey, ResourceTranscoder<?, ?>>();

    /**
     * Registers the given {@link com.bumptech.glide.load.resource.transcode.ResourceTranscoder} using the given
     * classes so it can later be retrieved using the given classes.
     *
     * @param decodedClass The class of the resource that the transcoder transcodes from.
     * @param transcodedClass The class of the resource that the transcoder transcodes to.
     * @param transcoder The transcoder.
     * @param <Z> The type of the resource that the transcoder transcodes from.
     * @param <R> The type of the resource that the transcoder transcodes to.
     */
    public <Z, R> void register(Class<Z> decodedClass, Class<R> transcodedClass, ResourceTranscoder<Z, R> transcoder) {
        factories.put(new MultiClassKey(decodedClass, transcodedClass), transcoder);
    }

    /**
     * Returns the currently registered {@link com.bumptech.glide.load.resource.transcode.ResourceTranscoder} for the
     * given classes.
     *
     * @param decodedClass The class of the resource that the transcoder transcodes from.
     * @param transcodedClass The class of the resource that the transcoder transcodes to.
     * @param <Z> The type of the resource that the transcoder transcodes from.
     * @param <R> The type of the resource that the transcoder transcodes to.
     */
    @SuppressWarnings("unchecked")
    public <Z, R> ResourceTranscoder<Z, R> get(Class<Z> decodedClass, Class<R> transcodedClass) {
        if (decodedClass.equals(transcodedClass)) {
            // we know they're the same type (Z and R)
            return (ResourceTranscoder<Z, R>) UnitTranscoder.get();
        }
        final ResourceTranscoder<?, ?> result;
        synchronized (GET_KEY) {
            GET_KEY.set(decodedClass, transcodedClass);
            result = factories.get(GET_KEY);
        }
        if (result == null) {
            throw new IllegalArgumentException("No transcoder registered for " + decodedClass + " and "
                    + transcodedClass);
        }
        return (ResourceTranscoder<Z, R>) result;
    }
}

这是一个缓存类,可以看出是通过17行register()方法缓存ResourceTranscoder对象,使用时通过31行get()方法取出。那么它是什么时候调用的register()方法呢。答案与之前一样,也是在Glide的构造器方法中,我们再看一下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);
    }

查看transcoderRegistry.register方法,注意第49行,当传入对象是GifBitmapWrapper.calss和GlideDrawable.class时,缓存的ResourceTranscoder对象是GifBitmapWrapperDrawableTranscoder,这个是transcoder的具体实现类。而且GifBitmapWrapperDrawableTranscoder里封装的ResourceTranscoder<Bitmap, GlideBitmapDrawable>对象的实现类是GlideBitmapDrawableTranscoder。

总结:

transcoder的具体实现类是GifBitmapWrapperDrawableTranscoder;

GifBitmapWrapperDrawableTranscoder里封装的ResourceTranscoder<Bitmap, GlideBitmapDrawable>对象的实现类是GlideBitmapDrawableTranscoder。

 

FixedLoadProvider封装的第三个对象是DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider,我们再来找找这个DataLoadProvider的具体实现是什么。

具体类的实现分析3:

创建DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider对象的glide.buildDataProvider()方法中传入了ImageVideoWrapper.class和GifBitmapWrapper.class。进入glide.buildDataProvider方法查看

    <T, Z> DataLoadProvider<T, Z> buildDataProvider(Class<T> dataClass, Class<Z> decodedClass) {
        return dataLoadProviderRegistry.get(dataClass, decodedClass);
    }

看到这里,是不是发现和分析transcoder的过程一样,dataLoadProvider的创建过程也确实跟transcoder一模一样,这里就不再跟后续的代码了,读者有兴趣的话可以自己查看。

直接写出分析的结论:

dataLoadProvider的具体实现类是ImageVideoGifDrawableLoadProvider

需要注意的是ImageVideoGifDrawableLoadProvider还封装了ImageVideoDataLoadProvider和GifDrawableLoadProvider,ImageVideoDataLoadProvider又封装了StreamBitmapDataLoadProvider,这几个类在后面也会使用到。

 

这里是初始化中最麻烦的一部分,对FixedLoadProvider封装的对象做一个总结:

到这里load方法就讲完了,里面具体实现类的分析内容是为了into方法做准备,into方法更为复杂。

三、into方法

load方法中返回了DrawableTypeRequest对象。

再写一下DrawableTypeRequest的继承关系:DrawableTypeRequest   ->   DrawableRequestBuilder   ->   GenericRequestBuilder

into方法的入口在DrawableRequestBuilder中:

    @Override
    public Target<GlideDrawable> into(ImageView view) {
        return super.into(view);
    }

直接调用了父类的into(ImageView 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));
    }

这里处理了一下异常和图片的scaleType,就进入了into(Y target)方法。这里先分析一下glide.buildImageViewTarget返回的target的具体实现是什么。

 

具体类的实现分析4:

glide.buildImageViewTarget()的入参,一个是ImageView,另一个transcodeClass是在DrawableRequestBuilder调用GenericRequestBuilder的构造器方法时传入的,它是GlideDrawable.class。

看一看glide.buildImageViewTarget方法:

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

跟进imageViewTargetFactory.buildTarget:

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

可以看到当传入的clazz是GlideDrawable.class时,返回的target是GlideDrawableImageViewTarget,它就是target的具体实现。

总结:

target的具体实现是GlideDrawableImageViewTarget。

 

确定了传入的参数类型,我们看看into(Y target)方法:

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

首先处理了异常情况,再获取到之前的Request,如果存在就回收掉。

最后的几句话是重点,先来看看buildRequest(target)方法

    private Request buildRequest(Target<TranscodeType> target) {
        if (priority == null) {
            priority = Priority.NORMAL;
        }
        return buildRequestRecursive(target, null);
    }

    private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) {
        if (thumbnailRequestBuilder != null) {
            if (isThumbnailBuilt) {
                throw new IllegalStateException("You cannot use a request as both the main request and a thumbnail, "
                        + "consider using clone() on the request(s) passed to thumbnail()");
            }
            // Recursive case: contains a potentially recursive thumbnail request builder.
            if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) {
                thumbnailRequestBuilder.animationFactory = animationFactory;
            }

            if (thumbnailRequestBuilder.priority == null) {
                thumbnailRequestBuilder.priority = getThumbnailPriority();
            }

            if (Util.isValidDimensions(overrideWidth, overrideHeight)
                    && !Util.isValidDimensions(thumbnailRequestBuilder.overrideWidth,
                            thumbnailRequestBuilder.overrideHeight)) {
              thumbnailRequestBuilder.override(overrideWidth, overrideHeight);
            }

            ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
            Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
            // Guard against infinite recursion.
            isThumbnailBuilt = true;
            // Recursively generate thumbnail requests.
            Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator);
            isThumbnailBuilt = false;
            coordinator.setRequests(fullRequest, thumbRequest);
            return coordinator;
        } else if (thumbSizeMultiplier != null) {
            // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
            ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
            Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
            Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator);
            coordinator.setRequests(fullRequest, thumbnailRequest);
            return coordinator;
        } else {
            // Base case: no thumbnail.
            return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
        }
    }

    private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
            RequestCoordinator requestCoordinator) {
        return GenericRequest.obtain(
                loadProvider,
                model,
                signature,
                context,
                priority,
                target,
                sizeMultiplier,
                placeholderDrawable,
                placeholderId,
                errorPlaceholder,
                errorId,
                fallbackDrawable,
                fallbackResource,
                requestListener,
                requestCoordinator,
                glide.getEngine(),
                transformation,
                transcodeClass,
                isCacheable,
                animationFactory,
                overrideWidth,
                overrideHeight,
                diskCacheStrategy);
    }

buildRequest(target)直接调用了buildRequestRecursive(),buildRequestRecursive中如果设置了thumbnail会走到前两个if里,没有设置直接走到最后的else,我们只分析最简单的情况。最后的else中直接返回了obtainRequest()获取的对象。obtainRequest方法调用GenericRequest.obtain静态方法。GenericRequest.obtain()入参传入了很多东西,像刚才分析了一大堆的loadProvider,设置的signature、placeholder、errorId、缓存等等。最后返回的是GenericRequest对象。

我们回到GenericRequestBuilder的into(target)方法,target.setRequest(request) 和 lifecycle.addListener(target)是其他的高级应用用到的。我们来看requestTracker.runRequest(request)方法

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

runRequest方法很简单,未暂停直接调用request的begin方法,暂停的话就加入挂起列表。

刚才分析了Request的具体对象是GenericRequest,那么直接来到GenericRequest的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));
        }
    }

begin方法里也是先处理了一下异常,先跳过中间部分,看下后面的部分,第17行调用了target.onLoadStarted()方法,这个target的具体实现是(具体实现类分析4)分析的GlideDrawableImageViewTarget,来看下它的onLoadStarted方法

    @Override
    public void onLoadStarted(Drawable placeholder) {
        view.setImageDrawable(placeholder);
    }

直接给ImageView设置了占位图。

这里就是在加载网络资源之前先给图片设置占位图,等网络资源加载完成后进行替换。

回到GenericRequest的begin方法,看中间的这部分代码。13行target.getSize方法中,传入了当前对象实现的接口类,最后也会走到onSizeReady方法中。流程走到了onSizeReady()中,来看一下

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

onSizeReady方法里,前面处理了一些基本信息。来看14行loadProvider.getModelLoader方法,我们在load方法中分析的实现类做的铺垫在这里就起到用处了,根据之前的分析loadProvider的实现类其实是FixedLoadPorvider,这里getModelLoader()得到的是ImageVideoModelLoader,15行modelLoader.getResourceFetcher()返回的是ImageVideoModelLoader的内部类ImageVideoFetcher。

21行loadProvider.getTranscoder()返回的是之前分析的GifBitmapWrapperDrawableTranscoder。

这里需要注意一下20行的streamLoader.getResourceFetcher()方法,初始化得到的streamFetcher后面加载数据时会用到,这里分析一下streamFetcher的具体实现类。

 

具体类的实现分析5:

查看ImageVideoModelLoader.getResourceFetcher()方法

public class ImageVideoModelLoader<A> implements ModelLoader<A, ImageVideoWrapper> {
    private static final String TAG = "IVML";

    private final ModelLoader<A, InputStream> streamLoader;
    private final ModelLoader<A, ParcelFileDescriptor> fileDescriptorLoader;

    public ImageVideoModelLoader(ModelLoader<A, InputStream> streamLoader,
            ModelLoader<A, ParcelFileDescriptor> fileDescriptorLoader) {
        if (streamLoader == null && fileDescriptorLoader == null) {
            throw new NullPointerException("At least one of streamLoader and fileDescriptorLoader must be non null");
        }
        this.streamLoader = streamLoader;
        this.fileDescriptorLoader = fileDescriptorLoader;
    }

    @Override
    public DataFetcher<ImageVideoWrapper> getResourceFetcher(A model, int width, int height) {
        DataFetcher<InputStream> streamFetcher = null;
        if (streamLoader != null) {
            streamFetcher = streamLoader.getResourceFetcher(model, width, height);
        }
        DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher = null;
        if (fileDescriptorLoader != null) {
            fileDescriptorFetcher = fileDescriptorLoader.getResourceFetcher(model, width, height);
        }

        if (streamFetcher != null || fileDescriptorFetcher != null) {
            return new ImageVideoFetcher(streamFetcher, fileDescriptorFetcher);
        } else {
            return null;
        }
    }

    static class ImageVideoFetcher implements DataFetcher<ImageVideoWrapper> {
        private final DataFetcher<InputStream> streamFetcher;
        private final DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher;

        public ImageVideoFetcher(DataFetcher<InputStream> streamFetcher,
                DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher) {
            this.streamFetcher = streamFetcher;
            this.fileDescriptorFetcher = fileDescriptorFetcher;
        }

    ...
        
}

由之前的分析可知,20行的streamLoader是在loadGeneric中创建的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对象,这个就是ImageVideoFether类中streamFetcher的具体实现。

 

回归到onSizeReady方法里,最后进入了engin.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());

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

engine.load()方法的前面部分都是对缓存的处理,跳过直接看最后的部分。

从第39行开始,根据工厂方法创建了一个EngineJob对象,又new了一个DecodeJob对象和一个EngineRunnable对象,后面调用EngineJob的start()方法,并传入EngineRunnable,看一下engine.start(runnable)

    public void start(EngineRunnable engineRunnable) {
        this.engineRunnable = engineRunnable;
        future = diskCacheService.submit(engineRunnable);
    }

这里也很简单,直接将EngineRunnable交给线程池运行,会直接走到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);
        }
    }

注意第10行resource = decode(),这里就是获取网络资源的地方

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

不是从缓存中加载,进入decodeFromSource查看

    private Resource<?> decodeFromSource() throws Exception {
        return decodeJob.decodeFromSource();
    }

返回了decodeJob.decodeFromSource()的结果,任务进行到了DecodeJob里,跟进DecodeJob的decodeFromSource方法

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

先看方法里的第一句,在这里获取网络资源,进入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;
    }

看核心的第一句话:第5行的fecher.loadData(priority),这里的fetcher是上面分析的ImageVideoModelLoader的内部类ImageVideoFetcher,进入ImageVideoFetcher的loadData()方法查看

        @Override
        public ImageVideoWrapper loadData(Priority priority) throws Exception {
            InputStream is = null;
            if (streamFetcher != null) {
                try {
                    is = streamFetcher.loadData(priority);
                } catch (Exception e) {
                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
                        Log.v(TAG, "Exception fetching input stream, trying ParcelFileDescriptor", e);
                    }
                    if (fileDescriptorFetcher == null) {
                        throw e;
                    }
                }
            }
            ParcelFileDescriptor fileDescriptor = null;
            if (fileDescriptorFetcher != null) {
                try {
                    fileDescriptor = fileDescriptorFetcher.loadData(priority);
                } catch (Exception e) {
                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
                        Log.v(TAG, "Exception fetching ParcelFileDescriptor", e);
                    }
                    if (is == null) {
                        throw e;
                    }
                }
            }
            return new ImageVideoWrapper(is, fileDescriptor);
        }

loadData()里先创建InputStream对象,终于看到了数据流对象,那么下面就会获取网络数据流了。

重点句第6行:is = streamFetcher.loadData(priority),又转移到了streamFetcher的loadData()方法里,这个streamFetcher的具体类是HttpUrlFetcher(具体实现类分析5),查看它的loadData方法

    @Override
    public InputStream loadData(Priority priority) throws Exception {
        return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
    }

    private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
            throws IOException {
        if (redirects >= MAXIMUM_REDIRECTS) {
            throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
        } else {
            // Comparing the URLs using .equals performs additional network I/O and is generally broken.
            // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
            try {
                if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
                    throw new IOException("In re-direct loop");
                }
            } catch (URISyntaxException e) {
                // Do nothing, this is best effort.
            }
        }
        urlConnection = connectionFactory.build(url);
        for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
          urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
        }
        urlConnection.setConnectTimeout(2500);
        urlConnection.setReadTimeout(2500);
        urlConnection.setUseCaches(false);
        urlConnection.setDoInput(true);

        // Connect explicitly to avoid errors in decoders if connection fails.
        urlConnection.connect();
        if (isCancelled) {
            return null;
        }
        final int statusCode = urlConnection.getResponseCode();
        if (statusCode / 100 == 2) {
            return getStreamForSuccessfulRequest(urlConnection);
        } else if (statusCode / 100 == 3) {
            String redirectUrlString = urlConnection.getHeaderField("Location");
            if (TextUtils.isEmpty(redirectUrlString)) {
                throw new IOException("Received empty or null redirect url");
            }
            URL redirectUrl = new URL(url, redirectUrlString);
            return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
        } else {
            if (statusCode == -1) {
                throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
            }
            throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
        }
    }

loadData()方法调用了loadDataWithRedirects方法,loadDataWithRedirects方法里看到了非常熟悉的网络请求类HttpURLConnection,到这里终于看到了网络请求的具体实现的地方。loadDataWithRedirects返回了获取的InputStream,在这里获取到数据流。

获取到数据流后,方法结构上再逐层向上返回

先回到IageVideoFetcher的loadData()方法中,获取到InputStream流后,又将InputStream封装为ImageVideoWrapper对象返回。

这里的fileDescriptor由fileDescriptorFetcher获取,而fileDescriptorFetcher从fileDescriptorLoader中取出,其实这里的fileDescriptorLoader其实为null,fileDescriptorFetcher也是null。读者可以从loadGeneric方法创建fileDescriptorLoader时查看。

再回到DecodeJob的decodeSource()方法,这里final A data = fetcher.loadData(priority),由上面可以知道,data的参数类型是ImageVideoWrapper,下面又调用了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;
    }

不分析有缓存的情况,到第7行loadProvider.getSourceDecoder().decode(data,width,heitght)方法里

loadProvider是一直说的FixedLoaderProvider,先来看下loadProvider.getSourceDecoder()会得到什么

    @Override
    public ResourceDecoder<T, Z> getSourceDecoder() {
        return dataLoadProvider.getSourceDecoder();
    }

返回了dataLoaderProvider.getSourceDecoder()的返回对象,FixedLoaderProvider中的dataLoadProvider是之前(具体实现类分析3)分析的ImageVideoGifDrawableLoadProvider,查看这个类的源码

public class ImageVideoGifDrawableLoadProvider implements DataLoadProvider<ImageVideoWrapper, GifBitmapWrapper> {
    private final ResourceDecoder<File, GifBitmapWrapper> cacheDecoder;
    private final ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> sourceDecoder;
    private final ResourceEncoder<GifBitmapWrapper> encoder;
    private final Encoder<ImageVideoWrapper> sourceEncoder;

    public ImageVideoGifDrawableLoadProvider(DataLoadProvider<ImageVideoWrapper, Bitmap> bitmapProvider,
            DataLoadProvider<InputStream, GifDrawable> gifProvider, BitmapPool bitmapPool) {

        final GifBitmapWrapperResourceDecoder decoder = new GifBitmapWrapperResourceDecoder(
                bitmapProvider.getSourceDecoder(),
                gifProvider.getSourceDecoder(),
                bitmapPool
        );
        cacheDecoder = new FileToStreamDecoder<GifBitmapWrapper>(new GifBitmapWrapperStreamResourceDecoder(decoder));
        sourceDecoder = decoder;
        encoder = new GifBitmapWrapperResourceEncoder(bitmapProvider.getEncoder(), gifProvider.getEncoder());

        //TODO: what about the gif provider?
        sourceEncoder = bitmapProvider.getSourceEncoder();
    }

    ...

    @Override
    public ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> getSourceDecoder() {
        return sourceDecoder;
    }

    ...
}

26行的getSourceDecoder()方法直接返回了sourceDecoder,来看下sourceDecoder的初始化。构造器中的bitmapProvider和gifProvider是在具体实现类分析3中分析的ImageVideoDataLoadProvider和GifDrawableLoadProvider。

第16行可以看到sourceDecoder是在构造器中进行的初始化,新建了一个GifBitmapWrapperResourceDecoder对象并赋值给它,这就是sourceDecoder的具体实现类。

找到了loadProvider.getSourceDecoder()的具体实现类,再查看GifBitmapWrapperResourceDecoder的decode方法

    @Override
    public Resource<GifBitmapWrapper> decode(ImageVideoWrapper source, int width, int height) throws IOException {
        ByteArrayPool pool = ByteArrayPool.get();
        byte[] tempBytes = pool.getBytes();

        GifBitmapWrapper wrapper = null;
        try {
            wrapper = decode(source, width, height, tempBytes);
        } finally {
            pool.releaseBytes(tempBytes);
        }
        return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;
    }

    private GifBitmapWrapper decode(ImageVideoWrapper source, int width, int height, byte[] bytes) throws IOException {
        final GifBitmapWrapper result;
        if (source.getStream() != null) {
            result = decodeStream(source, width, height, bytes);
        } else {
            result = decodeBitmapWrapper(source, width, height);
        }
        return result;
    }

    private GifBitmapWrapper decodeStream(ImageVideoWrapper source, int width, int height, byte[] bytes)
            throws IOException {
        InputStream bis = streamFactory.build(source.getStream(), bytes);
        bis.mark(MARK_LIMIT_BYTES);
        ImageHeaderParser.ImageType type = parser.parse(bis);
        bis.reset();

        GifBitmapWrapper result = null;
        if (type == ImageHeaderParser.ImageType.GIF) {
            result = decodeGifWrapper(bis, width, height);
        }
        // Decoding the gif may fail even if the type matches.
        if (result == null) {
            // We can only reset the buffered InputStream, so to start from the beginning of the stream, we need to
            // pass in a new source containing the buffered stream rather than the original stream.
            ImageVideoWrapper forBitmapDecoder = new ImageVideoWrapper(bis, source.getFileDescriptor());
            result = decodeBitmapWrapper(forBitmapDecoder, width, height);
        }
        return result;
    }

    private GifBitmapWrapper decodeGifWrapper(InputStream bis, int width, int height) throws IOException {
        GifBitmapWrapper result = null;
        Resource<GifDrawable> gifResource = gifDecoder.decode(bis, width, height);
        if (gifResource != null) {
            GifDrawable drawable = gifResource.get();
            // We can more efficiently hold Bitmaps in memory, so for static GIFs, try to return Bitmaps
            // instead. Returning a Bitmap incurs the cost of allocating the GifDrawable as well as the normal
            // Bitmap allocation, but since we can encode the Bitmap out as a JPEG, future decodes will be
            // efficient.
            if (drawable.getFrameCount() > 1) {
                result = new GifBitmapWrapper(null /*bitmapResource*/, gifResource);
            } else {
                Resource<Bitmap> bitmapResource = new BitmapResource(drawable.getFirstFrame(), bitmapPool);
                result = new GifBitmapWrapper(bitmapResource, null /*gifResource*/);
            }
        }
        return result;
    }

    private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
        GifBitmapWrapper result = null;

        Resource<Bitmap> bitmapResource = bitmapDecoder.decode(toDecode, width, height);
        if (bitmapResource != null) {
            result = new GifBitmapWrapper(bitmapResource, null);
        }

        return result;
    }

第2行的decode()方法,又进入到了另一个重载的decode方法里(15行),在重载的decode()方法中,17行if的判断语句中source.getStream()得到的就是刚才获取的InputStream,进入到decodeStream()里。

decodeStream()先读取2Kb的流,判断是否是gif类型,如果不是,就当成bitmap类型处理,我们分析bitmap类型的情况,会走到decodeBitmapWrapper()。decodeBitmapWrapper()中调用了bitmapDecoder的decode()方法。

这个bitmapDecoder的具体实现类是刚才初始化时传入的bitmapDecoder,它的具体实现类在ImageVideoDataLoadProvider中,通过ImageVideoDataLoadProvider.getSourceDecoder()获取,查看ImageVideoDataLoadProvider源码

public class ImageVideoDataLoadProvider implements DataLoadProvider<ImageVideoWrapper, Bitmap> {
    private final ImageVideoBitmapDecoder sourceDecoder;
    private final ResourceDecoder<File, Bitmap> cacheDecoder;
    private final ResourceEncoder<Bitmap> encoder;
    private final ImageVideoWrapperEncoder sourceEncoder;

    public ImageVideoDataLoadProvider(DataLoadProvider<InputStream, Bitmap> streamBitmapProvider,
            DataLoadProvider<ParcelFileDescriptor, Bitmap> fileDescriptorBitmapProvider) {
        encoder = streamBitmapProvider.getEncoder();
        sourceEncoder = new ImageVideoWrapperEncoder(streamBitmapProvider.getSourceEncoder(),
                fileDescriptorBitmapProvider.getSourceEncoder());
        cacheDecoder = streamBitmapProvider.getCacheDecoder();
        sourceDecoder = new ImageVideoBitmapDecoder(streamBitmapProvider.getSourceDecoder(),
                fileDescriptorBitmapProvider.getSourceDecoder());
    }

    ...

    @Override
    public ResourceDecoder<ImageVideoWrapper, Bitmap> getSourceDecoder() {
        return sourceDecoder;
    }

    ...
}

可以看到getSourceDecoder()返回的是sourceDecoder,而sourceDecoder在构造器中被创建,具体实现类是ImageVideoBitmapDecoder。查看该类,调用的sourceDecoder.decode(data,width,heitght)方法如下

    @Override
    public Resource<Bitmap> decode(ImageVideoWrapper source, int width, int height) throws IOException {
        Resource<Bitmap> result = null;
        InputStream is = source.getStream();
        if (is != null) {
            try {
                result = streamDecoder.decode(is, width, height);
            } catch (IOException e) {
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    Log.v(TAG, "Failed to load image from stream, trying FileDescriptor", e);
                }
            }
        }

        if (result == null) {
            ParcelFileDescriptor fileDescriptor = source.getFileDescriptor();
            if (fileDescriptor != null) {
                result = fileDescriptorDecoder.decode(fileDescriptor, width, height);
            }
        }
        return result;
    }

可以看到ImageVideoBitmapDecoder的decode方法还不是最终的decode实现,第7行又调用了streamDecoder的decode方法。先来找找这个streamDecoder的具体实现类是啥。

回到ImageVideoDataLoadProvider的构造器中,ImageVideoBitmapDecoder类的streamDecoder是在ImageVideoBitmapDecoder初始化时传入的streamBitmapProvider.getSourceDecoder(),这里的streamBitmapProvider是在ImageVideoDataLoadProvider构造器中传入的,它的具体实现在Glide的构造方法里,在具体实现类分析3中也有提到,它是StreamBitmapDataLoadProvider。

那么StreamBitmapDataLoadProvider的getSourceDecoder()返回了什么呢,查看一下:

public class StreamBitmapDataLoadProvider implements DataLoadProvider<InputStream, Bitmap> {
    private final StreamBitmapDecoder decoder;
    private final BitmapEncoder encoder;
    private final StreamEncoder sourceEncoder;
    private final FileToStreamDecoder<Bitmap> cacheDecoder;

    public StreamBitmapDataLoadProvider(BitmapPool bitmapPool, DecodeFormat decodeFormat) {
        sourceEncoder = new StreamEncoder();
        decoder = new StreamBitmapDecoder(bitmapPool, decodeFormat);
        encoder = new BitmapEncoder();
        cacheDecoder = new FileToStreamDecoder<Bitmap>(decoder);
    }

    ...

    @Override
    public ResourceDecoder<InputStream, Bitmap> getSourceDecoder() {
        return decoder;
    }

    ...
}

可以看到,getSourceDecoder()返回了decoder,decoder在StreamBitmapDataLoadProvider的构造器中初始化,具体类是StreamBitmapDecoder,这个是streamDecoder的具体实现。查看StreamBitmapDecoder代码

    @Override
    public Resource<Bitmap> decode(InputStream source, int width, int height) {
        Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
        return BitmapResource.obtain(bitmap, bitmapPool);
    }

这里才是decode方法的最终实现。可以看到decode方法,最终将InputStream转换为Bitmap,并封装成BitmapResource对象返回。

回到GifBitmapWrapperResourceDecoder的decodeBitmapWrapper方法中,bitmapDecoder.decode(toDecode, width, height)返回了封装好的BitmapResource对象,decodeBitmapWrapper再讲返回的BitmapResource封装成GifBitmapWrapper返回。

继续往回走,decodeStream和decode(ImageVideoWrapper source, int width, int height, byte[] bytes)同样返回GifBitmapWrapper对象,GifBitmapWrapper在decode(ImageVideoWrapper source, int width, int height)方法里又一次被封装成GifBitmapWrapperResource并返回。

回到DecodeJob,它的decodeFromSourceData(A data)和decodeSource()方法同样讲GifBitmapWrapperResource返回。

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

终于通过decodeSource()获取到了decoded,它是封装类GifBitmapWrapperResource。

下面走到了transformEncodeAndTranscode(decoded),这里将Resource<T>类型转换为了Resource<Z>类型,Resource<Z>类型的具体实现是Resource<GlideDrawable>,里面也会用到我们之前分析的GifBitmapWrapperDrawableTranscoder和GlideBitmapDrawableTranscoder,有兴趣的读者可以自己跟进去看一下,这里由于篇幅原因不再做具体的分析。

一直往回走,回到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);
        }
    }

resource从decode()方法中拿到Resource<GlideDrawable>,方法的最后可以看到run()方法的最后调用了onLoadComplete(resource)

    private void onLoadComplete(Resource resource) {
        manager.onResourceReady(resource);
    }

这里的manager就是EngineRunnable初始化时传入的对象,它其实是EngineJob,再来看看EngineJob的onResourceReady(source)方法

    @Override
    public void onResourceReady(final Resource<?> resource) {
        this.resource = resource;
        MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
    }

这里直接通过Handle发送了一个信息,我们找到信息处理的地方


        @Override
        public boolean handleMessage(Message message) {
            if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {
                EngineJob job = (EngineJob) message.obj;
                if (MSG_COMPLETE == message.what) {
                    job.handleResultOnMainThread();
                } else {
                    job.handleExceptionOnMainThread();
                }
                return true;
            }

            return false;
        }

可以看到走到了handleResultOnMainThread方法中

    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;

        // Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it
        // synchronously released by one of the callbacks.
        engineResource.acquire();
        listener.onEngineJobComplete(key, engineResource);

        for (ResourceCallback cb : cbs) {
            if (!isInIgnoredCallbacks(cb)) {
                engineResource.acquire();
                cb.onResourceReady(engineResource);
            }
        }
        // Our request is complete, so we can release the resource.
        engineResource.release();
    }

先看到listener.onEngineJobComplete,这个listener其实是Engine的实现类,创建EngineJobFactory时传入,再由EngineJobFactory创建EngineJobFactory,不过这里处理的是缓存的逻辑,我们可以继续往下看。

cbs缓存了一个callback集合,这里是在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) {

       ...

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

注意到12行engineJob.addCallBack(cb),这个cb是什么呢,再往前找,它就是GenericRequest,代码又往前面走了。

GenericRequest实现了ResourceCallback接口,并通过Engine.load()方法中把自己传了进来。

调用cb.onResourceReady(engineResource)本质是调用了GenericRequest的onResourceReady()方法,继续看一下

    @Override
    public void onResourceReady(Resource<?> resource) {
        if (resource == null) {
            onException(new Exception("Expected to receive a Resource<R> with an object of " + transcodeClass
                    + " inside, but instead got null."));
            return;
        }

        Object received = resource.get();
        if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
            releaseResource(resource);
            onException(new Exception("Expected to receive an object of " + transcodeClass
                    + " but instead got " + (received != null ? received.getClass() : "") + "{" + received + "}"
                    + " inside Resource{" + resource + "}."
                    + (received != null ? "" : " "
                        + "To indicate failure return a null Resource object, "
                        + "rather than a Resource object containing null data.")
            ));
            return;
        }

        if (!canSetResource()) {
            releaseResource(resource);
            // We can't set the status to complete before asking canSetResource().
            status = Status.COMPLETE;
            return;
        }

        onResourceReady(resource, (R) received);
    }

    /**
     * Internal {@link #onResourceReady(Resource)} where arguments are known to be safe.
     *
     * @param resource original {@link Resource}, never <code>null</code>
     * @param result object returned by {@link Resource#get()}, checked for type and never <code>null</code>
     */
    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);
        }
    }

onResourceReady()共有两个重载方法,第一个先做数据检验,然后调用第二个onResourceReady方法,再走到47行target.onResourceReady()中,这个target就是具体实现类分析4分析的GlideDrawableImageViewTarget,在GenericRequest创建时传入。

查看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();
    }

调用了super.onResourceReady方法,跟进去

    @Override
    public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) {
        if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
            setResource(resource);
        }
    }

    protected abstract void setResource(Z resource);

调用了setResource()方法,不过setResource()是一个抽象方法,还是由其子类实现,再回到GlideDrawableImageViewTarget中,查看setResource()

    @Override
    protected void setResource(GlideDrawable resource) {
        view.setImageDrawable(resource);
    }

给ImageView设置了drawable,终于将获取的网络图片加载到了ImageView上,到这里终于结束了。

 

总结

我们可以看到Glide在初始化时做了很多工作,并在过程中使用,中间涉及很多对象的传入和回调。这里还只是分析了最简单的一句代码:

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

而Glide还有很多复杂的应用,其编程难度和复杂度也可想而知。

查看源码对我来说有以下几点好处:

1. 对设计模式的应用更加了解。设计模式看着都比较简单,但想用好却是另外一回事。

2. 类的设计,功能划分上很明确,包括类的命名、方法命名就可以知道作用。Glide源码虽然复杂,但也都是成块地做事情,这部分代码负责一定的任务,另一部分代码负责另外的任务,分工明确。涉及到MVP、MVVM框架是上也是这样,让各自的分工更加明确。

3. 加深自己对框架的理解。

4. 最直接的好处是对Glide了解的更多了,应用起来也更简单。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Glide3.7.0源码详解 的相关文章

  • 如何在 HandlerInterceptorAdapter 中添加 HttpServletRequest 标头?

    我正在尝试将授权标头添加到我的请求中 作为我们切换环境时的临时解决方法 我试图在扩展 HandlerInterceptorAdapter 的拦截器中处理它 我使用 MutableHttpServletRequest 类制作here http
  • 无法从资源加载图片

    So I am trying to load a image file from a resource so that when I export my application into a jar file it could be use
  • 如何将 Java 地图转换为在 Scala 中使用?

    我正在开发一个 Scala 程序 该程序调用 Java 库中的函数 处理结果并生成 CSV 有问题的 Java 函数如下所示 Map
  • 以编程方式应用样式资源

    我没有找到一种以编程方式做到这一点的方法 所以我在这里发布这个问题 我也没有找到与此相关的任何问题 我有一个资源样式 在 res values styles xml 中定义 我想做的是使用 java 将这种样式应用到我正在操作的 View
  • Struts 1 到 Spring 迁移 - 策略

    我有一个legacy银行应用程序编码为Struts 1 JSP现在的要求是迁移后端 目前为 MVC to Springboot MVC 后续UI JSP 将迁移到angular Caveats 1 后端不是无状态的 2 会话对象中存储了大量
  • 如何在 JPA 和 Hibernate 中将数据库生成的列值定义为只读字段?

    使用 MariaDB 10 2 可以定义日期时间的默认值 例如创建和最后修改 我应该如何将此列作为只读字段访问 因为这个值应该只在数据库的控制之下 并且不应该从代码中修改 但我想在代码中读取这个属性 这很简单 只需设置insertable
  • Android - 正确使用 invalidateOptionsMenu()

    我一直在寻找很多invalidateOptionsMenu 我知道它的作用 但我想不出这种方法在现实生活中有用的任何例子 我的意思是 例如 假设我们要添加一个新的MenuItem to our ActionBar 我们可以简单地获取菜单on
  • InAppMessage 一旦显示就会自动消失

    您好 我最近将 InAppMessaging 添加到我的项目中 这似乎很容易集成 但对我来说并没有按预期工作 首先 我将其添加到 build gradle 中 implementation com google firebase fireb
  • 在 Android ADT Eclipse 插件中滚动布局编辑器

    有谁知道当布局编辑器的内容溢出一个 屏幕 时如何滚动这些内容 我说的是在设计时使用 ADT 布局编辑器 而不是在物理设备上运行时滚动 效果很好 关闭 Android 布局编辑器中的剪辑 切换剪辑 按钮位于 Android 布局编辑器的右上角
  • 如何在 Android 中保存 Edittext 中的文本而不丢失文本的粗体、斜体等功能

    我想做的就是从 Edittext 中获取文本 该文本具有粗体和斜体等功能 并将其保存在文本文件中 但是当我读回并显示它时 这些功能丢失了 它们不显示 如何通过将文本保存在文本文件或任何文件中来保持丰富的功能 您可以使用Html toHtml
  • 如何从intellij项目视图中隐藏不必要的文件?

    给定一个示例 gradle 项目 其项目结构如下所示 正如你所看到的 有很多东西你实际上不需要在想法中看到 但你需要它们存在 我知道下面被忽略的文件 文件夹类型Editor File Types但这些正在影响库和项目 idea 会在各处忽略
  • 使用单独的线程在java中读取和写入文件

    我创建了两个线程并修改了 run 函数 以便一个线程读取一行 另一个线程将同一行写入新文件 这种情况会发生直到整个文件被复制为止 我遇到的问题是 即使我使用变量来控制线程一一执行 但线程的执行仍然不均匀 即一个线程执行多次 然后控制权转移
  • 如何减去两个 XmlGregorianCalendar 对象来创建一个 Duration 对象?

    我想计算两个时间之间的差值XmlGregorianCalendar对象 从而创建一个Duration object 但我还没有找到执行减法的干净方法 你会怎么做 那应该是 DatatypeFactory newDuration xgc2 t
  • javax.media.jai 类的公共下载?

    这是一个非常简单的问题 我一直在寻找可以下载 javax media jai 库的地方 我找到了 jai imageio 库 但是我发现的所有其他 jai 内容要么已经过时 2008 年及之前 然后我遇到了登录屏幕 是否有 javax me
  • compileReleaseKotlin 失败并出现 java.lang.ClassNotFoundException:com.sun.tools.javac.util.Context

    我正在尝试使用 gradlew 通过终端构建我的 Android 项目 其中包含库模块 在 Android Studio 中 它编译并安装成功 但是当我尝试运行时 gradlew assembleDebug我得到以下堆栈跟踪 Using k
  • firebase中按范围查询

    我有一个食品价格范围滑块 根据滑块的最小值和最大值 我想显示此范围内的食品 滑块代码 multiSlider setOnThumbValueChangeListener new MultiSlider SimpleChangeListene
  • 画布:尝试使用回收的位图错误

    我是一个相当新的程序员 所以任何建议将不胜感激 我有一个类 每次调用它时都会在循环中运行 AsyncTask AsyncTask 看起来像这样 public class LoadImageTask extends AsyncTask
  • Java/MongoDB 按日期查询

    我将一个值作为 java util Date 存储在我的集合中 但是当我查询以获取两个特定日期之间的值时 我最终得到的值超出了范围 这是我的代码 插入 BasicDBObject object new BasicDBObject objec
  • 进程被杀死后不会调用 onActivityResult

    我有一个主要活动 Main 和另一个活动 Sub 由 Main 调用 startActivityForResult new Intent this SubActivity class 25 当我在 Sub 时 我终止该进程 使用任务管理器或
  • java中void的作用是什么?

    返回类型 方法返回值的数据类型 如果方法不返回值 则返回 void http download oracle com javase tutorial java javaOO methods html http download oracle

随机推荐

  • MySQL数据库综合案例1----创建“教务管理系统“数据库

    本节以教育管理系统为例来介绍数据库的创键和数据表的设计 案例需求 1 教务管理系统用来帮助高校学生选修课程 2 学生可以通过系统查看所有选修课的相关信息 包括课程名 学时 学分 也可以查看相关授课老师的信息 3 老师的信息包括教师姓名 性别
  • 用RBF网络实现单输入单输出非线性函数的拟合

    用RBF网络实现单输入单输出非线性函数的拟合 100个输入输出训练样本给定 101个输入输出测试样本给定 要求 使用1 10 1的RBF网络结构 输出节点有阈值 使用KNN K means聚类算法求出10个聚类中心和方差 聚类中心的初始值为
  • 数据库的c#实现 《添加数据》

    1 在SQL中 先创建数据库和表 create database Mycar use Mycar create table Car Id int primary key identity 1 1 设置编号 从1开始自增 每次加1 Title
  • html手风琴案例

    我们在前端开发的时候经常遇到鼠标经过的时候发生事件 鼠标离开的时候发生事件的效果 下面看看效果图 这下来看看我们的代码吧 html部分 div ul li a href img src images 1 jpg alt a li li a
  • Android 屏幕发生旋转对应的生命周期发生变化解析

    博主前些天发现了一个巨牛的人工智能学习网站 通俗易懂 风趣幽默 忍不住也分享一下给大家 点击跳转到网站 一 用户首次进入页面后执行的生命周期回调方法 二 屏幕发生旋转后 切换成了横屏 原先的活动被销毁 会重新创建新的活动 三 将横屏切回为竖
  • 栈和队列的基本操作(栈和队列的区别)

    数据结构中的栈与内存中的栈的不同 一 数据结构中的堆栈 在数据结构中的堆栈 实际上堆栈是两种数据结构 堆和栈 堆和栈都是一种数据项按序排列的数据结构 1 栈就像装数据的桶或箱子 我们先从大家比较熟悉的栈说起吧 它是一种具有后进先出性质的数据
  • 基于python 和 matlab的矩阵奇异值分解

    https www cnblogs com pinard p 6251584 html 可参考 一个 mxn的矩阵H可以分解为 U mxm S mxn V nxn 三个矩阵的乘积 这就是奇异值分解 S是一个对角矩阵 一般从大到小排列 S的元
  • Ubuntu16虚拟机调整窗口大小自适应windows7

    win7上ubuntu16虚拟机安装成功后 默认的虚拟机窗口比较小 需要适当调整 才能把虚拟机的屏幕放大 适合使用 以下介绍调整方法 安装vmware tools 启动虚拟机 在虚拟机管理界面上 主屏幕菜单 虚拟机 中点击 安装vmware
  • Xposed集成到Android系统

    XposedBridge 下载源码 然后编写Android mk文件放入源码根目录 LOCAL PATH call my dir include CLEAR VARS LOCAL DEX PREOPT false LOCAL MODULE
  • Qt编译,出现 first defined here,解决方法

    Qt编译 出现 first defined here 解决方法 一般出现这个问题是 pro文件多次包含了某个文件 解决方法 编译时候 找到是哪个文件出现 first defined here 这个错误 定位到文件 你再去 pro文件去找一下
  • 石头剪刀布游戏

    2 石头剪刀布游戏 编写程序模拟石头剪刀布人机对战 程序运行时 系统先出 生成一个随机数 若果为1表示系统出的是石头 2表示系统出的是剪刀 3表示系统出的是布 提示用户输入 同样的1 2 3分别表示石头 剪刀和布 系统分别打印出自己的出拳状
  • 如何进行探索性数据分析

    一般数据分析项目第一步都需要探索性数据分析 主要包括三个方面 使用描述性统计汇总数据 使用图标可视化数据 识别缺失值 通过上述三个方面分析 可以在执行假设检验或统计模型之前对数据集的分布情况有基本理解 并检测获得问题数据情况 下面通过示例说
  • Hexo插件机制分析

    CLI启动 有种流行的做法是把cli和实现分离 比如grunt cli和grunt hexo也是采取这种方式 hexo cli专门处理命令行 hexo才是具体的实现 可以像bash一样执行hexo cli的命令 启动脚本 usr bin e
  • Vue全家桶(一):Vue基础+Vue-Cli+Vue组件化+过渡动画

    目录 1 Vue概述 1 1 认识Vue 1 2 Vue的两核心 1 3 Vue的初体验 1 4 Vue的生命周期 2 Vue CLI Command Line Interface 3 Vue基本使用 3 1 传统开发模式对比 3 2 Vu
  • 关于blob类型的字段使用mybatis进行对mysql数据库的读取,修改properties文件后缀名为yml的问题,报不能bound statement的错的解决

    1 在处理blob类型的时候用到的类型处理器 org apache ibatis type BlobTypeHandler是MyBatis框架中的一个类型处理器 TypeHandler 用于处理数据库中的BLOB binary large
  • Linux 下 vim/Gvim配置

    Sections gt General gt VIM user interface gt Colors and Fonts gt Files and backups gt Text tab and indent related gt Vis
  • Turbo跨平台开源渲染引擎

    Turbo跨平台开源渲染引擎 基于Vulkan开发的跨平台渲染引擎 欢迎来参观 使用 提意见 o o Turbo开源引擎地址 Turbo开源引擎开发记录视频
  • NodeJs安装与全局配置

    1 首先我们要到nodejs官网 https nodejs org 上下载node 选择一个自己喜欢的版本下载 我这里选择的是最新版 2 下载完了就直接双击安装 安装过程中可以另外指定安装路径 我的安装路径是E FRONT nodejs 3
  • poi添加倾斜线

    添加倾斜线 org junit Test public void main1 try Workbook wb new XSSFWorkbook Sheet sheet wb createSheet XX销售对照表 CreationHelpe
  • Glide3.7.0源码详解

    基于的Glide版本 3 7 0 本文分析的是Glide最基本的一行代码 Glide with this load url into imageView 我们认准这一个功能点 来看看Glide默默为我们做了什么 这篇文章会分析这行代码背后的