你真的懂了Glide圖片框架了嗎?它來了,它來了,它帶着資源走來了...

簡言:

       之前寫過關於Glide的文章,都是一些如何使用的案例,比較注重使用了,沒有考慮它的源碼是如何實現的,今天爲大家講解一下源碼,從源碼的角度讓你瞭解Glide這個神奇的圖片框架。

1.Gilde 簡介

在泰國舉行的谷歌發佈者論壇上,谷歌爲我們介紹了一個叫Gilde的圖片加載庫,作者是bumptech。

這個庫被廣泛的運用在goole的開源項目中,包括2014年goole I/O大會上發佈的官方app

2.使用gilde的優點:

1)使用簡單

2)可支配度高,自適應程度高

3)支持常見圖片格式 jpg png gif webp

4)支持多種數據源,網絡,本地資源,assets等

5)高效緩存策略,支持Memory 和Disk圖片緩存,默認bitmap格式採用RGB_565內存至少減少一半

6)生命週期集成,根據activity/Fragment生命週期自動管理請求

7)高效處理bitmap 使用bitmap pool使bitmap複用,主動調用recycle回收需要回收的bitmap,減少系統回收壓力。

3.Glide的基本概念

Model它代表圖片的數據源,例如URL,本地文件,然後通過ModelLoader從數據源中獲取原始數據Data,在對原始數據Data進行Decoder解碼,解碼之後的資源Resource,並通過Transform進行圖片的裁剪轉換,轉換之後的資源我們稱之爲TransformResource,轉換之後我們還需要對圖像進行Transcode轉碼操作,轉碼之後的資源我們就稱之爲TranscodedResource,最後將我們所顯示的資源封裝成Target,並進行顯示。這就是Glide圖片加載的整體流程,

4.通過源碼進行分析

1)我們針對Glide3.7這個版本進行源碼分析簡介,這個版本相對於還是比較穩定的。

compile 'com.github.bumptech.glide:glide:3.7.0'

2)簡單實用的代碼:

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

我們幾乎都會這麼簡單的使用了,這裏沒什麼好講的了,接下來我們看一下他複雜的使用,我們並對每一個屬性進行講解,我們看代碼:

  Glide.with(getApplicationContext()) // 指定Context
                .load(url)// 指定圖片的URL
                .placeholder(R.mipmap.ic_launcher)// 指定圖片未成功加載前顯示的圖片
                .error(R.mipmap.ic_launcher)// 指定圖片加載失敗顯示的圖片
                .override(300, 300)//指定圖片的尺寸
                .fitCenter()//指定圖片縮放類型爲
                .centerCrop()// 指定圖片縮放類型爲
                .skipMemoryCache(true)// 跳過內存緩存
                .diskCacheStrategy(DiskCacheStrategy.NONE)//跳過磁盤緩存
                .diskCacheStrategy(DiskCacheStrategy.SOURCE)//僅僅只緩存原來的全分辨率的圖像
                .diskCacheStrategy(DiskCacheStrategy.RESULT)//僅僅緩存最終的圖像
                .diskCacheStrategy(DiskCacheStrategy.ALL)//緩存所有版本的圖像
                .priority(Priority.HIGH)//指定優先級.Glide將會用他們作爲一個準則,並儘可能的處理這些請求,但是它不能保證所有的圖片都會按照所要求的順序加載。優先級排序:IMMEDIATE > HIGH > NORMAL > LOW
                .into(imageView);//指定顯示圖片的Imageview

這裏說個題外話,注意到我們with方法傳遞的上下文了嗎?如果傳入的是當前activity的上下文,或者傳遞的是getApplicationContext,他們分別代表什麼?

當傳入的是activity被銷燬時,這個圖片加載的生命週期也會被銷燬,反之,如果山下文傳遞的是getApplicationContext時,只有程序被銷燬時,圖片加載的生命週期纔會被銷燬。

其它屬性的配置在代碼中已經做了標註,就不做過多的解釋了,我們下邊會對每一個參數進行源碼分析,我們針對幾個需要注意的點進行講解一下:

1)placeholder這個方法:不要加載網絡圖片,加載本地圖片,本身該方法主要解決網絡慢時,指定的佔位圖,如果在加載網絡圖片,該方法就沒有意義了,這裏注意一下。

2)override這個方法主要解決的就是內存浪費,防止內存溢出泄露等。

3)fitCenter,centerCrop,這兩個方法主要防止圖片失真,顯示模糊,做的處理。

 

3.源碼分析

1)首先我們看with方法的源碼:

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

   
    public static RequestManager with(Activity activity) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(activity);
    }

   
    public static RequestManager with(FragmentActivity activity) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(activity);
    }

 
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public static RequestManager with(android.app.Fragment fragment) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(fragment);
    }

爲什麼with這麼多重載方法,其實這個設計非常好,因爲傳入不同的參數,所對應的圖片加載的生命週期就會不同,所以才設置這麼多的重載方法,

我們繼續看retriever.get()方法:

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

(Util.isOnMainThread() && !(context instanceof Application),首先判斷是否是主線程,然後判斷傳遞過來的上下文,如果是Application的,它會走return getApplicationManager(context),我們進去看一下這個方法的源碼:

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

這麼不難看出,這是一個單例模式,爲了保證RequestManager這個唯一的實例,

所以這個with方法,的核心源碼主要就是獲取一個RequestManager這個實例,來負責管理我們的圖片請求。

2)load()方法

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

這個load方法也有很多重載方法,

DrawableTypeRequest<String>這個方法就是代表我們所有圖片的請求。我們看看源碼:

public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions {
    private final ModelLoader<ModelType, InputStream> streamModelLoader;
    private final ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader;
    private final RequestManager.OptionsApplier optionsApplier;

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

它是繼承DrawableRequestBuilder<ModelType>這個類:

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

    DrawableRequestBuilder(Context context, Class<ModelType> modelClass,
            LoadProvider<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable> loadProvider, Glide glide,
            RequestTracker requestTracker, Lifecycle lifecycle) {
        super(context, modelClass, loadProvider, GlideDrawable.class, glide, requestTracker, lifecycle);
        // Default to animating.
        crossFade();
    }

 這個類繼承GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>

主要就是一些參數的配置,例如縮略圖,佔位符,error圖等參數的設置。

我們返回DrawableTypeRequest方法,看fromString這個方法的源碼都做了些 什麼?

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

我們看一下loadGeneric這個方法,主要實現都在loadGeneric這個方法裏面:

private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
        ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
        ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
                Glide.buildFileDescriptorModelLoader(modelClass, context);
        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));
    }

這個方法裏面主要創建兩個ModelLoader,把我們的數據來源加載成原始數據,

3)into()方法(重要)

我們看一下into方法的實現:

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

我們看一下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是否爲null,這裏就不多說了,

我們看一下applyCenterCrop(),這個方法:

 void applyCenterCrop() {
        // To be implemented by subclasses when possible.
    }

它是一個空實現,我們看一下他的子類:

 @Override
    void applyCenterCrop() {
        centerCrop();
    }

我們接着看centerCrop這個方法:

 @SuppressWarnings("unchecked")
    public DrawableRequestBuilder<ModelType> centerCrop() {
        return transform(glide.getDrawableCenterCrop());
    }

 我們看一下transform怎麼操作的:

 /**
     * Transform resources with the given {@link Transformation}s. Replaces any existing transformation or
     * transformations.
     *
     * @param transformations the transformations to apply in order.
     * @return This request builder.
     */
    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> transform(
            Transformation<ResourceType>... transformations) {
        isTransformationSet = true;
        if (transformations.length == 1) {
            transformation = transformations[0];
        } else {
            transformation = new MultiTransformation<ResourceType>(transformations);
        }

        return this;
    }

這裏做了一些初始化的操作,對transform做了一些賦值。

 

我們回到into的方法,看一下return into(glide.buildImageViewTarget(view, transcodeClass));這個方法:

這裏是構建一個ImageViewTarget對象,我們看一下是如何構建的:

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

這個方法其實就是通過ImageViewTargetFactrory工廠來構建Target,我們看一下工廠主要做了什麼?

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

這個是不是主要做了Target加載哪一種圖片。

所以我們知道,這個BuildIamgeViewTarget主要做的就是讓我知道返回哪一種Target對象,

 

我們接着看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;
    }

這裏主要做的就是,創建一個加載圖片的Request,並對之前綁定的request進行刪除,並將新建的request綁定target,然後通過requestTracker這個request管理跟蹤器,做相應的request的處理。

到這裏簡單實用的Glide框架的源碼分析基本完事了,如果繼續深入關於內存緩存,或者磁盤緩存的源碼分析,請私信我,我將相關資源分享給您,into這個方法是Glide中重要的方法,如果深入探索還有很多東西,

 

 

 

 

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