Glide解析

前言

       移動應用幾乎都有圖片加載的需求,很多時候需要從遠程加載,有時也需要從本地加載,以前都往往是自己實現,這就需要考慮各種各樣的情況,比如緩存策略,需要綜合考慮內存使用,不同的圖片有不同時間,不同空間的緩存策略,其次是加載策略,是原圖加載,還是需要裁剪,是一次生成多種尺寸的縮略,還是不生成,有時還需要考慮網絡狀況來加載更小尺寸的圖。加載的圖片是否是gif,還需要對圖片的展示進程處理。有時還是從assert中加載的,或者從sd卡中加載的,不同的加載路徑怎麼拆分,怎麼纔有更好的代碼設計,這都是對代碼能力的一個考量。
       之後就逐步出現了很多的圖片加載框架,這些框架現在也應該使用在衆多的APP中,他們實現的更全面,功能更強大,有良好的擴展能力。把程序員從衆多的邏輯中解放出來,雖然使用更簡單了,但是我們要知其然也要知其所以然。接下來我們就梳理一下Glide的加載流程。

使用

       在分析之前我們先來看一個使用的樣例。這裏我們來加載一個遠程url的圖像,並且生成爲圓形頭像。

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

這裏佔位圖與錯誤圖都使用一個默認的資源。上面的代碼的意思很清楚明瞭,就是加載地址爲url的圖片,並縮放類型爲CenterCrop,之後transformation爲圓形,緩存策略爲所有尺寸緩存,佔位圖,錯誤圖都指定爲defaultAvatar,最終展示到傳入的view。

代碼跟蹤

       這裏我們就解析上面url的加載類型,其他的都是類似的。

第一步Glide.with(context)

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

       這裏主要是獲得一個RequestManager,首先獲取一個RequestManagerRetriever,RequestManagerRetriever是一個單例,get就是獲取RequestManagerRetriever的實例,這更加context獲取一個RequestManager,retriever.get(context)代碼如下:

public RequestManager get(Context context) {
    if (context == null) {
        throw new IllegalArgumentException("You cannot start a load on a null Context");
    } else if (Util.isOnMainThread() && !(context instanceof Application)) {
        if (context instanceof FragmentActivity) {
            return get((FragmentActivity) context);
        } else if (context instanceof Activity) {
            return get((Activity) context);
        } else if (context instanceof ContextWrapper) {
            return get(((ContextWrapper) context).getBaseContext());
        }
    }

    return getApplicationManager(context);
}

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

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

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

       上面的代碼只是根據不同的context來創建不同的requestManager,如果不在u線程,context不是Application則全局創建一個,比如這裏傳入的Context類型爲Activity,且在ui線程,那會加載參數類型爲Activity的get函數,這裏又會進入的fragmentGet函數:

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
    RequestManagerFragment current = getRequestManagerFragment(fm);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
        requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
        current.setRequestManager(requestManager);
    }
    return requestManager;
}

       這裏首先根據tag "com.bumptech.glide.manager"獲取Fragment,這裏的Fragment的類型RequestManagerFragment,獲取不到會創建一個家人的FragmentManager中,這裏假設首次調用,因此requestManager爲空,則會創建一個並且設置到Fragment,RequestManager構造如下:

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

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

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

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

       傳入的參數爲Context,lifecycle,已經請求樹結點,這個就不解析, 之後三參數的調用了無參數的構造函數,構造了一個請求跟蹤器RequestTracker,並在在lifecycle中加入了網絡連接的廣播,後面會根據網絡的鏈接是否重發請求:

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

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

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

       上述代碼就是當網絡從斷開到鏈接變化時,重發請求。

設置請求參數

       前面解析了RequestManager的構造過程,之後我們load了url,設置縮放類型,加入了緩存策略,設置了圖片裁剪方式,設置了佔位圖與加載失敗後的錯誤圖,這裏我們看看load url的過程。

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


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

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

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

       首先調用了load參數爲string,返回一個DrawableTypeRequest, 這裏注意參數類型,之後接着調用了fromString的load,而fromString又調用了loadGeneric, 我們來看看loadGeneric的過程:

1.Glide.buildStreamModelLoader(modelClass, context);
首先根據modelClass構造了streamModelLoader,這裏的modelClass類型爲String,之後我們會看看構造過程
2. Glide.buildFileDescriptorModelLoader(modelClass, context)
根據modelClass構造了fileDescriptorModelLoader,這裏的modelClass類型爲String,
根據上面的構造的兩種loader創建DrawableTypeRequest,
3.optionsApplier爲RequestManager中構造的,但是這裏它裏面調用的options爲空,因此不做任何操作。

       我們繼續看看buildStreamModelLoader,他裏面繼續調用瞭如下的函數:

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

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

       這裏繼續調用了三個參數的buildModelLoader, modelClass爲String, ResourceClass爲InputStream,context還是外部傳過來的上下文。裏面繼續調用了Glide.get(context).getLoaderFactory().buildModelLoader(),這裏首先獲取Glide的實例,Glide是一個單例這裏要單獨說說他們的實例話過程,後續會用到Glide實例的參數。等Glide分析後再回來解析DrawableTypeRequest的創建過程。

Glide初始化

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

public static Glide get(Context context) {
    if (glide == null) {
        synchronized (Glide.class) {
            if (glide == null) {
                Context applicationContext = context.getApplicationContext();
                List<GlideModule> modules = new ManifestParser(applicationContext).parse();

                GlideBuilder builder = new GlideBuilder(applicationContext);
                for (GlideModule module : modules) {
                    module.applyOptions(applicationContext, builder);
                }
                glide = builder.createGlide();
                for (GlideModule module : modules) {
                    module.registerComponents(applicationContext, glide);
                }
            }
        }
    }

    return glide;
}

       這裏實例化雙重檢查,因爲我們前面已經看到了可能會多線程加載,ui線程,後臺線程同時加載,雙重檢查構造單例。首先解析AndroidManifest中配置的Meta_data節點,key爲GlideModule,這裏我們沒有配置。之後構造一個GlideBuilder,主要是設置了context,在之後調用createGlide生成一個Glide實例。createGlide如下:

Glide createGlide() {
    if (sourceService == null) {
        final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
        sourceService = new FifoPriorityThreadPoolExecutor(cores);
    }
    if (diskCacheService == null) {
        diskCacheService = new FifoPriorityThreadPoolExecutor(1);
    }

    MemorySizeCalculator calculator = new MemorySizeCalculator(context);
    if (bitmapPool == null) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            int size = calculator.getBitmapPoolSize();
            bitmapPool = new LruBitmapPool(size);
        } else {
            bitmapPool = new BitmapPoolAdapter();
        }
    }

    if (memoryCache == null) {
        memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
    }

    if (diskCacheFactory == null) {
        diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }

    if (engine == null) {
        engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
    }

    if (decodeFormat == null) {
        decodeFormat = DecodeFormat.DEFAULT;
    }

    return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}

       主要是設置各種參數:

  • sourceService,首先設置一個先進先出的ThreadPoolExecutor
  • diskCacheService, 磁盤緩存先進先出的Executor
  • bitmapPool, bitmap緩存,現在一般都是api11以上,因此是LruBitmapPool
  • memoryCache 內存緩存
  • diskCacheFactory, 磁盤緩存工廠
  • engine,加載管理緩存引擎
  • decodeFormat,解碼格式,默認RGB_565,佔用兩個字節

       最後根據上訴的參數調用Glide的構造函數:

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

    dataLoadProviderRegistry = new DataLoadProviderRegistry();

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

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

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

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

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

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

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

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

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

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

這裏設置的參數後面都會用到,可以進行反查

       可以看到不僅設置了衆多的參數,還register了衆多的ModelLoaderFactory,註冊過程就不詳細解析了,只是爲了說明初始化了這些參數,現在我們繼續回到之前的Glide.buildStreamModelLoader(modelClass, context)。

buildStreamModelLoader

       上面中斷的過程主要是因爲buildStreamModelLoader用到了Glide初始化中的參數,這裏我們繼續往下看。他先調用了getLoaderFactory()獲得loaderFactory,loaderFactory是在Glide初始化創建的,並且註冊了衆多的ModelLoaderFactory。之後調用了buildModelLoader生成一個ModelLoader:

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

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

       這裏modelClass爲String,resourceClass爲InputStream,首先在cache中獲取,第一次調用cache中是沒有的,因此會調用getFactory(modelClass, resourceClass)獲得ModelLoaderFactory,會根據modelClass, resourceClass來獲取對應的ModelLoaderFactory,我們去Glide的構造函數中看看註冊爲String與InputStream的Factory是什麼?

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

       因此這裏的ModelLoaderFactory爲StreamStringLoader.Factory(), 因此我們看看他的Build函數:

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

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

       這裏又繼續調用了factories.buildModelLoader(Uri.class, InputStream.class),只不過第一個參數已經轉變成了Uri類型,因此我們查查註冊爲Uri與InputStream的ModelLoaderFactory,因此又會調用到StreamUriLoader.Factory()的build函數,這裏又級聯調用了factories.buildModelLoader(GlideUrl.class, InputStream.class),因此最終又調用到了HttpUrlGlideUrlLoader.Factory(),最終返回了HttpUrlGlideUrlLoader。

buildFileDescriptorModelLoader

       buildFileDescriptorModelLoader的整個流程與buildStreamModelLoader類似,因此這個就不展開了。

DrawableTypeRequest

       構造了streamModelLoader與fileDescriptorModelLoader後根據這些參數構造了DrawableTypeRequest,DrawableTypeRequest首先調用了DrawableRequestBuilder的構造函數:

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

       這裏有一個buildProvider的過程,根據參數創建了FixedLoadProvider。我們又回到最初設置參數的地方,load url,前面只是解析了fromString,還沒有設置最終的url參數,最後根據DrawableTypeRequest設置各種參數,比如url,設置bitmapTransform參數,各種參數就不展開了。

加載過程

       我們的調用樣例中最後一步是into(view),我們可以看到裏面調用了super.into(view)也就是GenericRequestBuilder的into函數,代碼如下:

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

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

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

       首先判斷view是否爲空,如果爲空,拋出異常,之後判斷是否設置了ScaleType,根據設置的效果來處理。最後繼續調用into函數,不過先將View重新build成ViewTarget,調用:

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

// 
public class ImageViewTargetFactory {

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

       這裏imageView就是傳入的view,transcodedClass的值又是什麼吶? 前面我們得到了DrawableTypeRequest,他的父類爲DrawableRequestBuilder,DrawableRequestBuilder的父類爲GenericRequestBuilder,我們在DrawableTypeRequest的構造函數中調用了DrawableRequestBuilder,這裏我們傳入了transcodedClass,爲GlideDrawable.class,因此最終這裏的返回的是GlideDrawableImageViewTarget,我們繼續看into函數。

public <Y extends Target<TranscodeType>> Y into(Y target) {
    Util.assertMainThread();
    if (target == null) {
        throw new IllegalArgumentException("You must pass in a non null Target");
    }
    if (!isModelSet) {
        throw new IllegalArgumentException("You must first set a model (try #load())");
    }

    Request previous = target.getRequest();

    if (previous != null) {
        previous.clear();
        requestTracker.removeRequest(previous);
        previous.recycle();
    }

    Request request = buildRequest(target);
    target.setRequest(request);
    lifecycle.addListener(target);
    requestTracker.runRequest(request);

    return target;
}

       首先判斷是不是ui線程,androd中各種控件的展示都是在UI線程操作的,之後判斷target是否爲空,爲空拋出異常,之後獲取上一次request,這裏第一次調用previous爲空,之後調用buildRequest構造一個request,buildRequest首先設置了優先級,之後繼續調用buildRequestRecursive這裏就不貼代碼了,先判斷縮略請求是否爲空,多尺寸縮略是否爲空,否則就調用obtainRequest獲的request,obtainRequest裏面又繼續調用GenericRequest.obtain獲得request,obtain中的參數就是之前構造時候設置的參數。這裏就不展開了。

       之後將request設置到target,lifecycle添加一個listener,最後requestTracker調用runRequest開始執行該request:

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

       首先將request加入到請求隊列中,如果當前頁面不是paused狀態,則開始調用request的begin函數,否則加入pending隊列,當狀態變爲resume時,繼續執行,我們繼續看看begin函數:

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

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

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

       這裏實際調用的是GenericRequest的begin函數,首先記錄開始時間,之後設置status爲WAITING_FOR_SIZE,等待獲取圖片的尺寸,如果當前寬高已經獲取到了,調用onSizeReady,否則調用getSize,getSize在尺寸確定後會調用回調SizeReadyCallback的onSizeReady函數,之後如果還沒有加載完成活失敗,則先顯示佔位圖。

注意展位圖只能是resource資源

       我們繼續看看他是怎麼獲得圖片的尺寸的,getSize會調用父類的getSize,這裏是一個接口,實際調用的是ViewTarget的getSize,爲什麼是ViewTarget,還記得我們之前buildTarget過程吧!這裏又繼續調用了 sizeDeterminer.getSize(cb):

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

       這裏add系統OnPreDrawListener回調,根據系統回調來確定的寬高。

onSizeReady

       我們來着重看看onSizeReady函數:

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

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

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

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

       先判斷狀態是不是等待獲取尺寸,如果不是表示已經執行過了,之後將狀態設置Running狀態,之後重新計算寬高,不過這裏sizeMultiplier爲1,尺寸其實不變,之後獲取ModelLoader,ModelLoader我們在上面已經分析過了。

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

       之後調用modelLoader.getResourceFetcher(model, width, height),那這裏首先調用的StreamStringLoader的getResourceFetcher,可以發現他根本沒有getResourceFetcher這個函數,他實際調用的是StringLoader的getResourceFetcher:

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

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

       modle就是傳入的url,如果url爲空直接返回空,如果傳入串是’/’開頭的,表示從本地讀取,會在串的前面加入“file”標識,否則轉換爲Uri,獲取Scheme,接着調用uriLoader.getResourceFetcher(uri, width, height),這裏的uriLoader爲StreamUriLoader,因此調用的是StreamUriLoader的getResourceFetcher,最終調用的是UriLoader的getResourceFetcher:

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

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

    return result;
}

       如果scheme爲file,content,android:resource都表示從本地獲取,否則如果urlLoader不爲空,切scheme爲http或者https,則調用urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height),這裏的urlLoader爲HttpUrlGlideUrlLoader,因此最終調用的是HttpUrlGlideUrlLoader的getResourceFetcher:

@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
    // GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time spent parsing urls.
    GlideUrl url = model;
    if (modelCache != null) {
        url = modelCache.get(model, 0, 0);
        if (url == null) {
            modelCache.put(model, 0, 0, model);
            url = model;
        }
    }
    return new HttpUrlFetcher(url);
}

       前擾萬繞終於到HttpUrlFetcher了,之後獲取transcoder,transcoder是前面我們構造DrawableTypeRequest生成的,這裏resourceClass爲GifBitmapWrapper,transcodedClass爲GlideDrawable,獲取出來爲空,之後調用engine.load函數,參數爲之前構造的參數:

public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
        DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
        Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
    Util.assertMainThread();
    long startTime = LogTime.getLogTime();

    final String id = fetcher.getId();
    EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
            loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
            transcoder, loadProvider.getSourceEncoder());

    。。。。

    EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
    DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
            transcoder, diskCacheProvider, diskCacheStrategy, priority);
    EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
    jobs.put(key, engineJob);
    engineJob.addCallback(cb);
    engineJob.start(runnable);

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);
}

       這裏代碼太長,將其他代碼給註釋了,我們主要看會構建一個engineJob,之後構建一個decodeJob,構建一個EngineRunnable,之後調用addCallback設置回調ResourceCallback,最後調用start將Runnable submit,最終會回到EngineRunnable的run函數:

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

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

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

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

       我們繼續跟蹤decode函數,獲取圖片資源,獲取成功後會通知ResourceCallback的onResourceReady,同時view顯示,我們先來看看decode函數,會先判斷是否從緩存中獲取,否則調用decodeFromSource(),最終調用DecodeJob的decodeFromSource():

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

       先decodeSource,在對resource進程變換,decodeSource如下:

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

       終於又看到了fetcher了,這裏fetcher就是前面生成的HttpUrlFetcher,因此調用的是HttpUrlFetcher的loadData,這裏主要是網絡操作了獲取一個InputStream流。之後對獲取的InputStream進行decodeFromSourceData:

private Resource<T> decodeFromSourceData(A data) throws IOException {
    final Resource<T> decoded;
    if (diskCacheStrategy.cacheSource()) {
        decoded = cacheAndDecodeSourceData(data);
    } else {
        long startTime = LogTime.getLogTime();
        decoded = loadProvider.getSourceDecoder().decode(data, width, height);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Decoded from source", startTime);
        }
    }
    return decoded;
}

       先判斷是否cache,則先緩存data,這裏我們設置的需要緩存,則會調用cacheAndDecodeSourceData:

private Resource<T> cacheAndDecodeSourceData(A data) throws IOException {
    long startTime = LogTime.getLogTime();
    SourceWriter<A> writer = new SourceWriter<A>(loadProvider.getSourceEncoder(), data);
    diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Wrote source to cache", startTime);
    }

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

       先構造一個SourceWriter,參數是loadProvider.getSourceEncoder()與data,這裏第一個參數是我們之前構造DrawableTypeRequest生成的,也就是ImageVideoGifDrawableLoadProvider的getSourceEncoder,主要是解析是否是gif圖片,並寫入緩存,之後返回寫入的文件。

       這些都執行完成後調用transformEncodeAndTranscode,這裏主要代用我們傳入的Transformation對資源進行轉換,比如裁剪成圓形,或者帶有圓角的等:

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

    writeTransformedToCache(transformed);

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

       這先調用transform對資源進程轉換,之後在調用transcode對資源進行轉換,這些都是之前我們設置的參數進行操作的。

       整個過程完成後,會調用onLoadComplete,主要是通知callback告訴Resource已經準備好了,最終會回到GenericRequest,該GenericRequest就是我們之前創建的Request,最終會調用GenericRequest的onResourceReady:

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

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

    notifyLoadSuccess();

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

       這裏最終會調用我們之前創建的Target,這裏的target就是前面build創建的GlideDrawableImageViewTarget,因此會調用GlideDrawableImageViewTarget的onResourceReady:

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

       這裏先判斷該圖片是否是動畫類型,也就是gif類型,這個是根據之前decode解碼出來的,那一部分沒有詳細解析,如果是這設置動畫的執行次數。

結束語

       到此一次執行的大致流程就結束了,其中還有很多細小的枝節沒有詳細分析,不過可以根據流程看一遍。

       諸如其他比如從本地加載,或者從assert加載流程都是一樣的。

       目前Glide對本地resource是不能進行transform的,因此如果有需要可以對他進行改造,不過Glide官方也說了會從4.0開始支持。

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