Glide 學習筆記

入行一年半的android程序員,半個月前的一個上午還在跟同事討論發(fā)不發(fā)年終獎,下午就被裁了。找工作快兩個禮拜了,連面試的都沒有,更別說找個好工作了。估計再呆下去真得抑郁癥了

同理,上來先噴一下,橫線之后開始寫內容


Glide的基本用法

Glide.with(this).load("").into();

Glide :: with

創(chuàng)建了一個RequestManagerRetriever 的實例,調用了get方法

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

RequestManagerRetriever :: get

先檢查一下線程,如果不在主線程,執(zhí)行get方法 。 ps: 不過我記得,加載圖片,是不能放在線程里的,這里卻放過了線程

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

RequestManager :: supportFragmentGet

  • 從FragmentManager中取出當前頁面的fragment
    (SupportRequestManagerFragment 這個fragment主要的功能就是同步activity的生命周期)
  • 要是activity沒有這個fragment的話,pendingSupportRequestManagerFragment這兒拿
  • 還沒有的話創(chuàng)建一個fragment

ps:這里有個問題,每次創(chuàng)建完之后,發(fā)一個handler,把存儲在pendingSupportRequestManagerFragments中的fragment干掉了,不知道是干嘛的,猜測是避免重復創(chuàng)建frgment的一個手段吧

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

RequestManager

RequestManager管理了每個acitivity上的圖片請求,RequestManager 含有了activity的生命周期,并且含有一個RequestTracker這樣的請求棧,這個棧是一個包含有請求(request)的arrayList

RequestManager :: load

還記得上面說到的request嗎?這里就根據傳入的資源去構建一個request build,這里就是構建了一個DrawableTypeRequest 。在RequestManager 中有各種load,load調用了各種from,但都調用了一個loadGeneric方法

public DrawableTypeRequest < Integer > load(Integer resourceId) {
    return (DrawableTypeRequest < Integer > ) fromResource().load(resourceId);
}

RequestManager :: loadGeneric

從代碼上看,主要創(chuàng)建了兩個modelLoader 和調用了 optionsApplier.apply。(這里要打一個大大的問號了......)這段代碼不是很懂,結合這注釋看,總之這里創(chuàng)建了DrawableTypeRequest (具體過程先不管了),調用父類的load方法,也就是GenericRequestBuilder的load

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

GenericRequestBuilder :: into

GenericRequestBuilder中的into方法,首先進行了線程的檢查,等異常的處理和transform的幾種縮放處理,之后統(tǒng)一調用了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));
}

GenericRequestBuilder :: into(target)

這個方法主要看target有沒有請求,若有的話清掉,在構建一個請求,添加到生命周期,并執(zhí)行請求

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

通過obtainRequest 去獲得Request

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

RequestTracker :: runRequest

將請求添加到請求隊列中,如果沒有暫停,將Request開始,并且添加到掛起的隊列

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

GenericRequest :: begin

begin 方法中onSizeReady() 是主要的方法,與此之外,這里還調用了target的一些方法,主要是添加占位和錯誤的圖片,基本上就是調用了view 的設置圖片,就不粘代碼了。主要還是看onSizeReady

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

主要的是這一段

loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this);

調用loadFromCache從內存加載,若返回值為空再次從活動的資源中加載,若再次為空查看jobs是否提交過任務,若沒有提交則創(chuàng)建EngineRunnable,并將任務提交到engineJob中

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

看一下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);
    }
}

是從緩存中拿到還是從資源中拿到,應該在decodeFromCache指的是緩存,FromSource拉取資源。緩存是通過DiskLruCache進行獲取的。繼續(xù)跟到decodeFromSource中,最后到了decodeSource這個方法中

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

最主要的是fetcher的loadData方法,從注釋上來看,fetcher是一個用于加載資源的接口,實現這個接口的類很多, 取一個最常用的 HttpUrlFetcher 試著去看看

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;
}
Paste_Image.png

最后關注了一下loadDataWithRedirects中的聯網請求

HttpUrlFetcher ::loadDataWithRedirects

在代碼中使用了HttpURLConnection作為網絡請求等一些屬性

Paste_Image.png

基本上glide的流程都走了一遍,但每個模塊都沒有深入的研究,有時間再去看看幾個有疑問地方的代碼實現。
這里貼一張網絡圖片,記錄一下glide的總體設計

Paste_Image.png

Glide 怎么加載的okhttp作為網絡請求的 ?

在一個app,為了保持網絡請求的一致性,通常也會把Glide的數據加載換成與本來項目中的網絡請求一致的框架,glide也支持這些,這是官方的文檔 https://github.com/bumptech/glide/wiki/Integration-Libraries
比如我用的ide是android studio, 當我添加了這個依賴

dependencies { 
compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar' //compile 'com.squareup.okhttp:okhttp:2.2.0'
}

gradle會自動在AndroidManifest.xml中添加一下這段標簽,ant 或者 maven就得手動添加

<meta-data android:name="com.bumptech.glide.integration.okhttp.OkHttpGlideModule" android:value="GlideModule" />

在Glide創(chuàng)建RequestManager時,RequestManager的構造方法會調用Glide的get方法。 這里讀取了ManifestParser的標簽,去找到GlideModule標簽對應的android:name
在代碼指定的是com.bumptech.glide.integration.okhttp.OkHttpGlideModule
最后通過ManifestParser::parseModule()去創(chuàng)建一個GlideModule, 這里實現GlideModule接口的是OkHttpGlideModule

在for循環(huán)中, 調用了applyOptions方法,但是什么都沒干,最后調用了registerComponents方法,創(chuàng)建了一個OkHttpUrlLoader.Factory(), 去注冊一個 GenericLoaderFactory loaderFactory。在上面看的源碼中,看不太懂的modeloader()那里, 正好獲得了這個OkHttpUrlLoader。

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

private static GlideModule parseModule(String className) {
    Class < ?>clazz;
    try {
        clazz = Class.forName(className);
    } catch(ClassNotFoundException e) {
        throw new IllegalArgumentException("Unable to find GlideModule implementation", e);
    }

    Object module;
    try {
        module = clazz.newInstance();
    } catch(InstantiationException e) {
        throw new RuntimeException("Unable to instantiate GlideModule implementation for " + clazz, e);
    } catch(IllegalAccessException e) {
        throw new RuntimeException("Unable to instantiate GlideModule implementation for " + clazz, e);
    }

    if (! (module instanceof GlideModule)) {
        throw new RuntimeException("Expected instanceof GlideModule, but found: " + module);
    }
    return (GlideModule) module;
}
public class OkHttpGlideModule implements GlideModule {@Override public void applyOptions(Context context, GlideBuilder builder) {
        // Do nothing.
    }

    @Override public void registerComponents(Context context, Glide glide) {
        glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
    }
}
public class OkHttpGlideModule implements GlideModule {
@Override
 public void applyOptions(Context context, GlideBuilder builder) {
        // Do nothing.
    }

    @Override
 public void registerComponents(Context context, Glide glide) {
        glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
    }
}

Glide 怎么實現對生命周期的處理?

Glide 使用創(chuàng)建一個fragment的方式,監(jiān)視了activity的生命周期。

FragmentManager fm = getSupportFragmentManager();
FeedFragment current = (FeedFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null){    
    current = new FeedFragment();   
    fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
}

這樣就可以同步activity的生命周期

  • 當啟動一個應用

Activity onCreate:
Fragment onAttach:
Fragment onCreate:
Fragment onCreateView:
Fragment onActivityCreated
Fragment onStart:
Activity onStart:
Activity onResume:
Fragment onResume:

  • 按下home

Fragment onPause:
Activity onPause:
Fragment onStop:
Activity onStop:

  • 重新喚醒

Fragment onStart:
Activity onStart:
Activity onResume:
Fragment onResume:

  • 退出應用

Fragment onPause:
Activity onPause:
Fragment onStop:
Activity onStop:
Fragment onDestroyView:
Fragment onDestroy:
Fragment onDetach:
Activity onDestroy:

Glide怎么判斷的圖片大?。?/h3>

根據上面的流程,我定位到了GenericRequest :: begin方法

這里先對寬高,進行判斷,若不合法,走target的getSize, getSize也會對寬高判斷,失敗了就會拋出異常(throw new IllegalArgumentException("You cannot set the tag id more than once or change" + " the tag id after the first request has been made");)我準備沿著代碼邏輯向前走,看看是哪里來的overrideWidth。

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

GenericRequestBuilder中對寬和高進行初始化,初始化的值是-1,發(fā)現以下這些類都調用了對寬高進行了賦值,這些類都是GenericRequestBuilder的子類,都會調用GenericRequestBuilder :: override()
方法

Paste_Image.png

RequestManager :: loadGeneric創(chuàng)建了DrawableTypeRequest, DrawableTypeRequest是DrawableRequestBuilder的子類, DrawableRequestBuilder這個繼續(xù)調用父類的構造。最后找到Glide :: 的buildImageViewTargetImageViewTargetFactory :: buildTarget
DrawableImageViewTarget分析,一直跟到ViewTarget的構造, 這里創(chuàng)建了一個SizeDeterminer,最上面的代碼target.getSize(this);實際上調用了就是這個SizeDeterminer的getSize.看了一圈, 又回到了GenericRequest類;
所以,在GenericRequest的begin中,當指定了override時, 直接調用onSizeReady;沒有則調用了target(target理解成view就好)getSize,看當前的view是否已經有寬高了,若沒有則去監(jiān)聽view的寬高,再去調用onSizeReady

ViewTarget :: getSize

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

Glide 設置縮略圖之后回莫名其妙的‘閃’一下

在加載一個列表頁的時候,通常會添加一個占位圖,并設置crossfade這個屬性,能讓顯示更加平穩(wěn),顯示效果也比較好看。但出現一個問題。圖片加載的時候, 圖片不僅僅回做透明度的變化,并且大小也改變了, 顯示效果頁比較難看。

GlideDrawableImageViewTarget::onResourceReady

最終圖片加載會走到這里,中間一堆注釋先不看(看不懂)

看一下父類的onResourceReady方法

Paste_Image.png

能看到這里new 了一個TransitionDrawwable, 這個drawable正式能顯示漸變動畫的drawable

getCurrentDrawable 是view當前的drawale

再看下 #### GenericRequest::begin()

當未完成未失敗的情況下,加載了placeholder,這時候view顯示的占位圖試placeholder, currentDrawable卻是占位圖的, 但進行動畫之后glide回按照imageview的寬高裁剪圖片,這樣一來,必然會出現閃一下的這種情況了

Glide本地緩存是什么樣的?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容