RxCache的簡單源碼分析

  1. RxCache本身的一些宏觀機制和自己的構思還是比較契合的:
    • 最基本的一點,都將所有數據回調形式統一爲Observable.
    • 複合key,tag + 附加信息的方式,但是RxCache做的更爲複雜,還支持keyGroup的概念。
    • 數據提取模塊化,每種數據提取方式都是獨立的,並且是可擴展的,當然也是Obserable化的。
    • 對返回的數據進行包裝從而得以返回更多元數據,比如source。
    • 都採取了disk + memory的緩存方案,但是我這邊直接使用了DualCache,擴展性上是不如RxCache提供的Persistence接口的。
  2. 不一樣的地方:

    • RxCache這種基於Proxy+註解進行擴展的方式要更加靈活一些,不過個人感覺如果是項目內部使用,讓RxJava來維護數據提取模塊也是可以接受的。
    • RxCache對於緩存過期的實現通過註解來實現,應該算是更加優雅,不過這樣的話,過期時間其實是不可改變的了。
    • RxCache本身對數據的提取內部做了一個定死的優先級排列:先取緩存,緩存無效,才取真正提供的數據源,儘管提供了evict做強制的數據源提取,但是不是很靈活,但是在我們實際項目中,是有很多其他case色。
    • RxCache因爲把數據源提取全部外放,可能導致對複數個數據源的case不能很好兼容。
  3. RxCache是一個基於RxJava的異步數據緩存庫,其角色定位不是一個數據加載和緩存控制總管,而像一個service提供者和功能裝飾者. 一句話總結: 我不生產數據,我只是數據的”搬運(二次處理+控制)”工。

  4. RxCache提供了using函數(這個設計比較有意思,RxCache本身更像一個Factory,通過using來生產出一個被處理過的DataProvider來給用戶使用)來將數據提取的某些職責分離了出來(參數名中的Provider表明了 provide數據(即生成數據) 這一功能被從RxCache中分離,嚴格意義上講,這種分離是必須的,讓每個外部的數據提供者都能做適配)。
  5. RxCache的using函數接受一個Class對象(classProviders), 並且返回的是該Class一個被Proxy了的實例:

    1. 爲了使用Java提供的Proxy機制,需要一個實現了InvocationHandler的實例,在這裏這個實例的類型是ProxyProviders,其實例化需要的參數有RxCache構造使用的Builder和using函數參數的Class
    2. 然後基於上面構造的ProxyProviders實例,yongJava的Proxy機制返回一個所有函數調用會被攔截到ProxyProviders實例並實現了classProviders接口的對象
  6. 那麼從上面看,RxCache**自己能做手腳的地方就在上面生成的InvocationHandler(ProxyProviders)實例了**:

    • 首先看ProxyProviders的關鍵函數invoke()(InvocationHandler所規定的接口,所有對被Proxy對象的函數調用都會被攔截到該接口上)
    • 該函數的實現是調用Observable.defer(Func0<Observable<T>> observableFactory)構造一個Observable,defer的作用是爲每個subscribe了該Observable的observer根據函數中提供的工廠構造方法構造一個Observable.
    • 那麼這裏的工廠構造方法就是processorProviders.process(proxyTranslator.processMethod(method, args)
    • 上面的processorProviders是ProxyProviders對象構造時生成的一個ProcessorProviders對象。proxyTranslator同樣也是構造時進行了實例化。
    • ProcessorProviders本身的定位是 Entry point for RxCache,其process函數可以根據提供的ConfigProvider信息返回一個Observable
    • 先關注ProxyTranslator:
      • 上面調用的是其processMethod(method, args)函數,method是被攔截的方法對應的Method對象,而args則是調用該函數時傳遞的一系列參數。
      • processMethod(Method method, Object[] objectsMethod)會根據輸入的Method對象和函數參數生成一個ConfigProvider作爲返回結果。
        • 首先調用loadConfigProviderMethod(method)來獲取一個ConfigProvider(注意其名字是prev).
          • loadConfigProviderMethod(Method method)本質上是從ProxyTranslator內部的一個緩存(Map<Method, ConfigProvider>)中根據傳入的Method對象查找是否之前已經有處理過的緩存結果(很多涉及到反射類分析的庫都會這樣做),如果有,那麼直接返回(所以上面返回的才叫prev,因爲不是最新的結果)
          • 否則,就構造一個ConfigProvider實例,構造的參數包括:
            1. getProviderKey(method): 返回方法的名稱
            2. getLifeTimeCache(method): 如果方法上有LifeCache註解,那麼返回其duration屬性。
            3. requiredDetailResponse(method):判斷方法返回值的類型(method.getReturnType())是否是Observable(不是則會拋異常,因爲RxCache一個要求是外部提供的Provider接口的返回值類型都應該是Observable), 並且Observable的範型類型是否繼承自Reply(RxCache自己規定的數據返回包裝格式)
            4. getExpirable(method):方法上是否有Expirable註解表明其返回的數據會過期,如果是false,那麼代表數據永遠不會過期
            5. isEncrypted(method):方法上是否有Expirable註解,是否加密。
        • 然後利用該ConfigProvider的部分信息結合其他信息構造出一個configProvider作爲返回結果. 使用瞭如下新的構造信息:
          1. dynamicKey -> getDynamicKey(method, objectsMethod), 基於method和此次調用的參數生成一個用於標記這種特殊(具體參數不一樣,那麼key當然應該不一樣)請求的數據key
          2. dynamicKeyGroup -> getDynamicKeyGroup(method, objectsMethod), 基本同上,不過group範圍更大,對應一批特定條件。
          3. loaderObservable -> getLoaderObservable(method, objectsMethod) 一個關鍵的對象,這個對象本身承載了真正的數據加載功能,並且是外部提供的,是數據的“源”,而RxCache只是對這個源做了二次處理,比如如果數據沒過期那麼顯然不需要從“源”那裏直接取,用RxCache自己本身緩存的數據即可
          4. EvictProvider -> evictProvider(method, objectsMethod)
        • 上面4個對象的取值都是基於getObjectFromMethodParam(Method method, Class expectedClass, Object[] objectsMethod)函數:
          • 該函數遍歷Method被調用時輸入的參數,如果有參數的類型滿足expectedClass.isAssignableFrom(objectParam.getClass()),並且滿足這種條件的參數只有一個(多餘1個直接拋異常,因爲破壞了RxCache的約定),那麼就返回該參數對象。說白了就是根據實現約定好的參數類型,從被攔截方法的調用參數中找到和約定類型一致的對象
        • 那麼其實上面4個對象本質上講就是外部調用時傳入的參數對象
        • 根據上面的分析,ConfigProvider裏面封裝的信息就是該方法本身的屬性(名稱,相關注解)以及方法的調用參數(只不過Proxy機制不能直接拿到各個類型對應的值,invoke傳遞參數時直接傳一個Object數組,沒有常規函數調用那樣的類型對應,還需要遍歷處理一遍)
    • 再回到ProxyProviders的defer的Observable構造函數中,現在可以知道proxyTranslator.processMethod(method, args)最終返回的是一個對本次方法調用的信息的集合體(既包括了方法本身的靜態信息,也包括了本次調用參數的動態信息)
    • 然後processorProviders根據這部分信息返回一個真正給外部使用的Observable。
    • ProxyProviders的processorProviders本身實例化的過程比較複雜(涉及到依賴注入),其最終對應的實現類是ProcessorProvidersBehaviour,其process函數也是調用Observable的defer來爲每個observer生成一個Observable。
      • process的核心調用是getData(final ConfigProvider configProvider):
        • 第一步使用twoLayersCache.retrieve(configProvider.getProviderKey(), configProvider.getDynamicKey()………..),本質就是基於此次調用的信息(都已經被提取被填充到了configProvider中)從twoLayersCache中提取出之前的緩存信息(如果有的話),緩存針對的粒度是方法本身+方法調用參數
        • 用Observable.just將得到的數據轉化爲一個Observable。
        • 後面緊接一個map operator,其轉化路徑是:<Record, Observable<Reply>>,從twoLayersCache.retrive返回的Record類型轉化爲Observable<Reply>
        • 如果發現從twoLayersCache中得到的數據是有效的,並且本次調用沒有指定evict,那麼表明緩存中的數據是直接可以用的, 直接將返回的Record封裝爲一個Replay通過just進行消息的續傳
        • 如果發現緩存中沒有數據或者數據不可用,那麼調用getDataFromLoader(), 故名思意,使用外部調用者提供的loader(數據的源)來提取數據
        • getDataFromLoader()的調用主要包括: 調用提供的loader來取數據,取完數據以後保存到twoLayersCache中做緩存
        • 通過讀緩存或者數據源最終返回數據(以Reply的包裝形式)以後會再接一個flatMap <Observable<Reply>, Observable<Object>> 結合 getReturnType(configProvider, reply)函數將得到的數據做一次DeepCopy然後如果方法中要求了requiredDetailedResponse, 那麼返回一個封裝了Data的Reply(這裏的Detail就好理解了,意思是除了Data本身,還返回一些元數據,包括數據的來源以及數據是否加密),如果沒有要求返回詳細信息,則直接返回數據本身。
        • 最終,呈現給外部使用者的,是一個Data或者封裝了Data的Reply,並且都是通過Observable來完成數據回調的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章