1. 程式人生 > >Glide原始碼分析(二)---執行

Glide原始碼分析(二)---執行

吐槽

為啥csdn的排名一直沒有什麼變化啊emmmmm,這幾天5公里夜跑跑的越來越快,越來越接近25分鐘了,趕緊學習啊啊啊啊,自己真的還差好遠啊啊啊

看原始碼的感想

看到郭神部落格上面的寫的:

那麼,雖然原始碼的複雜程度是外在的不可變條件,但我們卻可以通過一些技巧來提升自己閱讀原始碼的能力。這裡我和大家分享一下我平時閱讀原始碼時所使用的技巧,簡單概括就是八個字:抽絲剝繭、點到即止。應該認準一個功能點,然後去分析這個功能點是如何實現的。但只要去追尋主體的實現邏輯即可,千萬不要試圖去搞懂每一行程式碼都是什麼意思,那樣很容易會陷入到思維黑洞當中,而且越陷越深。因為這些龐大的系統都不是由一個人寫出來的,每一行程式碼都想搞明白,就會感覺自己是在盲人摸象,永遠也研究不透。如果只是去分析主體的實現邏輯,那麼就有比較明確的目的性,這樣閱讀原始碼會更加輕鬆,也更加有成效。

之前自己看Okhttp的原始碼的時候,就是看著這些類頭都大了,感覺自己智商可能不夠的感覺,覺得寫出這些的東西的人都好厲害啊,但是,突然一想自己看原始碼是為了什麼 往小裡面說,是更好理解這個框架,讓自己得心應手,面試時候可以講 往大里面講,就是知道具體的框架主體邏輯是什麼,主體功能的實現的流程

所以,我好像並不需要現在就把所有的類啊引數啊都搞清楚,我只需要知道大概的流程是什麼樣的,這才是我看原始碼的目標,因為這些東西都是一個團隊寫的,自己一個人怎麼可能很快把這個框架完全搞懂啊。

閱讀的邏輯

這個框架的幹嘛的?-----》主要的功能是什麼?------》關鍵的跳轉方法是什麼? Glide是一個載入圖片的框架 最最主要的程式碼就是

Glide.with(上下文).load(圖片資源).into(控制元件);

然後我們先把這三個方法搞清楚邏輯

with()方法

我們首先看下這個方法,當時在用with方法的時候,我們發現這個傳入的引數可以是Activity,也可以是fragment,Context這些,所以我們可以判定這個方法肯定有很多個過載方法 進入原始碼我們看下這個方法,就發現了4個過載方法

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(android.app.Fragment fragment) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(fragment); } publc static RequestManager with(Fragment fragment) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(fragment); }

雖然有四個過載方法,但是我們發現這幾個實現的邏輯都是如出一轍 都是呼叫RequestManagerRetriever.get()方法返回一個RequestManagerRetriever物件 然後再呼叫RequestManagerRetriever物件的get()方法獲取RequestManager物件

所以,這個邏輯還是蠻清楚的

接著,我們繼續看下這個RequestManagerRetriever的例項get()方法

public static RequestManagerRetriever get() {
    return INSTANCE;
}

// Visible for testing.
RequestManagerRetriever() {
    handler = new Handler(Looper.getMainLooper(), this /* Callback */);
}

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

然後我們看了下get()方法的幾個過載就分為兩種情況

  • 傳入Application型別的引數
  • 傳入非Application型別的引數//activity啊,Fragment什麼的

先看下第一種情況下的:Application引數

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

如果在Glide.with()方法中傳入的是一個Application物件,那麼在這裡就會呼叫這個方法 然後在方法的最後呼叫這個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.事件,我們必須強制管理器使用ApplicationLifecycle開始恢復
                applicationManager = new RequestManager(context.getApplicationContext(),
                        new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
            }
        }
    }

    return applicationManager;
}

就是獲取一個RequestManager物件,把註釋的幾句話谷歌翻譯下,我們就發現這塊就是它自動就是和應用程式的生命週期是同步的,如果應用程式關閉的話,Glide的載入也會同時終止

然後我們再看下第二種情況:非非Application引數 看下他們傳入的引數

  • get(FragmentActivity activity)
  • get(Fragment fragment)
  • get(Activity activity)
  • get(android.app.Fragment fragment)

然後我們發現這些方法都會呼叫這兩個方法:

supportFragmentGet(fragment.getActivity(), fm);
fragmentGet(activity, fm);

然後我們進這個兩個方法裡面看下

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

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

然後我們總結髮現:

  • 所有的流程都是一樣的,都是向當前的Activity裡面新增一個一個隱藏的Fragment,呼叫的方法就是supportFragmentGet(fragment.getActivity(), fm);和fragmentGet(activity, fm);這兩個
  • supportFragmentGet(fragment.getActivity(), fm);是V4包下的,fragmentGet(activity, fm)是app包下的
  • 之所以要新增一個Fragment的原因是,Glide需要知道這個活動的生命週期,不能你這個活動就要被關閉了,但現在用Glide才載入圖片進行了一半唉,所以肯定要通知Glide這個活動要掛了,當然不應該。可是Glide並沒有辦法知道Activity的生命週期,於是Glide就使用了新增隱藏Fragment的這種小技巧,因為Fragment的生命週期和Activity是同步的,如果Activity被銷燬了,Fragment是可以監聽到的,這樣Glide就可以捕獲這個事件並停止圖片載入了
  • 如果我們是在非主執行緒當中使用的Glide,那麼不管你是傳入的Activity還是Fragment,都會被強制當成Application來處理

主要流程圖//畫的醜emmmmmmm 在這裡插入圖片描述

小結一下: 第一個with()方法的原始碼,其實就是為了得到一個RequestManager物件而已,然後Glide會根據我們傳入with()方法的引數來確定圖片載入的生命週期

load()方法

這個方法就是選擇載入資源的,肯定也是很多過載方法 看郭神上面講了URL這種的,我們也就看下這種的,因為這種的我們用的多

首先

我們在上面with()方法返回的是個RequestManager物件,所以很清楚的知道了load方法就是在這個RequestManager類裡面 然後我們去看下這個原始碼:

public class RequestManager implements LifecycleListener {
    private final Context context;
    private final Lifecycle lifecycle;
    private final RequestManagerTreeNode treeNode;
    private final RequestTracker requestTracker;
    private final Glide glide;
    private final OptionsApplier optionsApplier;
    private DefaultOptions options;

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

        /sts();
        for (RequestManager requestManager : treeNode.getDescendants()) {
            requestManager.pauseRequests();
        }
    }

    /**
     * Restarts any loads that have not yet completed.
     *
     * @see #isPaused()
     * @see #pauseRequests()
     */
    modelLoader) {
        return new ImageModelRequest<T>(modelLoader);
    }

    /**
     * Returns a request builder that uses the given
     * {@link com.bumptech.glide.load.model.stream.StreamByteArrayLoader} to fetch an {@link java.io.InputStream} for
     * loading Bitmaps.
     *
     * @param modelLoader The byte array loader.
     */
    public ImageModelRequest<byte[]> using(StreamByteArrayLoader modelLoader) {
        return new ImageModelRequest<byte[]>(modelLoader);
    }

    /**
     * Returns a new request builder that uses the given {@link ModelLoader} to fetch a
     * {@link ParcelFileDescriptor} for loading video thumbnails.
     *
     * @param modelLoader The model loader to use.
     * @param <T> The type of the model.
     */
    public <T> VideoModelRequest<T> using(final FileDescriptorModelLoader<T> modelLoader) {
        return new VideoModelRequest<T>(modelLoader);
    }

    /**
     * Returns a request builder to load the given {@link java.lang.String}.
     * signature.
     *
     * @see #fromString()
     * @see #load(Object)
     *
     * @param string A file path, or a uri or url handled by {@link com.bumptech.glide.load.model.UriLoader}.
     */
    public DrawableTypeRequest<String> load(String string) {
        return (DrawableTypeRequest<String>) fromString().load(string);
    }
    public DrawableTypeRequest<String> fromString() {
    return loadGeneric(String.class);
}
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核心的就這幾個函式 就把這個load(String string)這個方法找到,然後把URL傳進去,然後就是呼叫fromString()方法,這個方法就繼續呼叫loadGeneric()方法,並指定引數為String.class

然後我們看下這塊的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));
}

然後我們發現

  • Glide.buildStreamModelLoader()方法Glide.buildFileDescriptorModelLoader()方法獲取一個ModelLoader物件

  • ModelLoader物件是用於載入圖片的,而我們給load()方法傳入不同型別的引數,這裡也會得到不同的ModelLoader物件

  • 才傳入的引數是String.class,因此最終得到的是StreamStringLoader物件,它是實現了ModelLoader介面的

  • loadGeneric()方法是要返回一個DrawableTypeRequest物件的,因此在loadGeneric()方法的最後又去new了一個DrawableTypeRequest物件,然後把剛才獲得的ModelLoader物件,還有一大堆雜七雜八的東西都傳了進去

然後我們去看下這塊的DrawableTypeRequest的作用

DrawableTypeRequest(Class<ModelType> modelClass
            
           

相關推薦

Glide原始碼分析()---執行

吐槽 為啥csdn的排名一直沒有什麼變化啊emmmmm,這幾天5公里夜跑跑的越來越快,越來越接近25分鐘了,趕緊學習啊啊啊啊,自己真的還差好遠啊啊啊 看原始碼的感想 看到郭神部落格上面的寫的: 那麼,雖然原始碼的複雜程度是外在的不可變條件,但我們卻可以通過一些

Glide原始碼分析)——從用法來看之load&into方法

上一篇,我們分析了with方法,文章連結: https://blog.csdn.net/qq_36391075/article/details/82833260 在with方法中,進行了Glide的初始化,建立了RequesManger,並且綁定了生命週期,最終返回了一個Reques

精盡MyBatis原始碼分析 - SQL執行過程()之 StatementHandler

> 該系列文件是本人在學習 Mybatis 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋([Mybatis原始碼分析 GitHub 地址](https://github.com/liu844869663/mybatis-3)、[Mybatis-Spring 原始碼分析 GitHub 地址

NSQ原始碼分析()—— Topic

Topic是NSQ非常重要的概念,本次主要講述Topic的獲取、新建、Topic中訊息的輪詢、Topic中訊息的來源、Topic的刪除和退出以及Topic的暫停和取消暫停 topic的相關操作主要在nsq/nsqd/topic.go中 首先看下Topic結構體 type Topic st

Glide原始碼分析(一)從用法來看之with方法

繼續啃原始碼,用過Glide的人,肯定都覺得它好好用,我們一般只需要幾行程式碼,就可以達到我們想要的效果,可以在這個背後是什麼呢?就需要我們來看了。 我一般看原始碼,我喜歡先從用法來看,然後一步一步的再細扣,所以就先從用法來看Glide的整體流程。 用過Glide的人,用下面這段

Django rest framework 許可權操作(原始碼分析)

知識回顧  這一篇是基於上一篇寫的,上一篇謝了認證的具體流程,看懂了上一篇這一篇才能看懂, 當用戶訪問是 首先執行dispatch函式,當執行當第二部時: #2.處理版本資訊 處理認證資訊 處理許可權資訊 對使用者的訪問頻率進行限制 self

Glide 原始碼分析(一):圖片壓縮

關於圖片的那點事兒 Q: 一張大小為 55KB, 解析度為 1080 * 480 的 PNG 圖片,它載入近記憶體時所佔的大小是多少呢? 圖片記憶體大小 圖片佔用記憶體大小 = 解析度 * 畫素點大小 其中資料格式不同畫素點大小也不同: ALPHA_8: 1B RGB_565: 2B

spring原始碼分析():外部配置檔案的載入

@PropertySource 和@Profile 簡介: @PropertySource:用在類上,載入一個(*.properties)的配置檔案 @Profile:用在類或者方法上,指定元件在哪個環境的情況下才能被註冊到容器中,不指定,任何環境下都能註冊這個元件 使用案例1-

spring原始碼分析():bean元件賦值

文章目錄 常用註解 @Value和@PropertySource 介紹 使用案例 @AutoWired、@Qulifer 、@Primary 介紹 使用總結

RxJava2原始碼分析之just、fromArray、fromIterable

     Observable.just:接收1個以上,10個以下的引數,然後逐個發射。         Observable.fromArray:接收一個數組,從陣列中一個一個取出來發射。  

最新版ffmpeg原始碼分析:transcode()函式

還是先看一下主函式吧:(省略了很多無關大雅的程式碼) int main(int argc, char **argv) { OptionsContext o = { 0 }; int64_t ti; //與命令列分析有關

Glide原始碼分析(一)---基本

吐槽 中秋節啊,自己身為一個單身程式猿又不回家,還是在實驗室學個新的框架吧,畢竟這個框架很實用,之前自己都還沒用過,就很尷尬。 那中秋快樂啊 本文思維導圖 Glide是什麼 答曰:一種安卓平臺上的圖形載入框架,一種很好很好用的框架 然後網上說了下picasso

原始碼分析AsyncTask執行流程和原理

本篇文章將從大體上分析AsyncTask的原理,不會涉及過多的細節。AsyncTask現在已經不再流行了,但作為學習還是要了解下。 使用方法 下面是AsyncTask一般的使用場景,相信使用過AsyncTask的人都不會覺得陌生 class MyAsyncTask ex

Glide 原始碼分析(一)

Glide原始碼分析 寫在前面 在專案開發中一直使用Glide進行圖片的載入顯示,知道如何使用,但不清楚Glide內部是如何實現的。所以一直想看看Glide的原始碼實現是如何做的,終於有時間和機會了,寫下來做個筆記,也給大家學習下。 Glide載入基本步驟 一般

ArrayList的原始碼分析()

上篇文章給大家介紹了arraylist集合原始碼的一些屬性和擴容方式add方法,接下來再和大家來聊聊這個集合的一些原始碼 首先看看remove的方法,這個方法有兩個,一個是根據下標刪除物件,一個是根據物件刪除 rangeCheck(index);是一個判斷index引數的規範的,如果太長或者

深入原始碼分析Java執行緒池的實現原理

程式的執行,其本質上,是對系統資源(CPU、記憶體、磁碟、網路等等)的使用。如何高效的使用這些資源是我們程式設計優化演進的一個方向。今天說的執行緒池就是一種對CPU利用的優化手段。 網上有不少介紹如何使用執行緒池的文章,那我想說點什麼呢?我希望通過學習執行緒池原理,明白所有

java集合原始碼分析()---ListIterator與Iterator

吐槽 早上起來刷牙洗臉洗頭髮後,正準備去上課,然後發現今天早上好像不上課emmmmmm,然後就早上繼續看下java集合方面的原始碼了吼吼吼 Iterator 上次好像說過一點這個,然後繼續複習下這個 還是先看下官方文件 然後我們發現這個東西就是個功能很少但很

React原始碼分析()-元件的初始渲染

上一篇文章講到了React 呼叫ReactDOM.render首次渲染元件的前幾個過程的原始碼, 包括建立元素、根據元素例項化對應元件, 利用事務來進行批量更新. 我們還穿插介紹了React 事務的實現以及如何利用事務進行批量更新的實現. 這篇文章我們接著分析後面的過程,

原始碼分析篇之String原始碼分析

    前面已經分析過String原始碼為什麼是不可變的,同時通過我們常用的String的相關的類StringBuffer和StringBuilder,我們可以發現String類中欄位名被定義為了final型別,這樣的話將只能被賦值一次。接下來,繼續看String原始碼實現的

JDK1.8集合框架原始碼分析-------------LinkedList

0.LinkedList特點    0.1) 刪除或新增效率高:不會移動資料元素,只會維護內部節點的關係    0.2) 查詢慢: 在其內部沒有下標,也即沒有索引,需要使用for迴圈遍歷,LInkedList為了提高效率,