Android數(shù)據(jù)層架構(gòu)的實現(xiàn) 上篇

本文首發(fā)于微信公眾號——世界上有意思的事,搬運(yùn)轉(zhuǎn)載請注明出處,否則將追究版權(quán)責(zé)任。微信號:a1018998632,交流qq群:859640274

最近我們app的服務(wù)器吃不消了,所以我在為服務(wù)器增加緩存層之后,又想到在app端進(jìn)行二級緩存以減少app對服務(wù)器的訪問。我想很多app應(yīng)該在項目的初期架構(gòu)的時候就考慮到了這個問題,但是我當(dāng)時開發(fā)這個app的時候完全不懂架構(gòu)和設(shè)計模式,所以對性能根本沒有考慮,導(dǎo)致了現(xiàn)在服務(wù)器經(jīng)常崩潰。關(guān)于服務(wù)器的優(yōu)化之后有時間再說,今天我們先來看看如何對一個app的數(shù)據(jù)的請求和緩存進(jìn)行架構(gòu)。

一.數(shù)據(jù)請求的分類、數(shù)據(jù)的可緩存性分析和數(shù)據(jù)同步##

如何對請求進(jìn)行歸類?這是個問題,在Http請求里設(shè)定了八種請求的方式,但是顯然并不適合我們這里的情況。我對我們app中的數(shù)據(jù)請求進(jìn)行歸類之后進(jìn)行了一下兩種分類

  • 1.根據(jù)需要對請求做那些操作進(jìn)行分類:
    • 1.GET:需要去內(nèi)存、硬盤和服務(wù)器中取數(shù)據(jù)的請求,請求時可以提供參數(shù),但是參數(shù)并不會上傳到內(nèi)存、硬盤和服務(wù)器中。
    • 2.INSERT:需要將數(shù)據(jù)上傳至內(nèi)存、硬盤和服務(wù)器中,返回的數(shù)據(jù)根據(jù)具體情況判斷是否需要緩存到內(nèi)存或者硬盤中。
    • 3.NONE:向服務(wù)器進(jìn)行驗證操作,必須和服務(wù)器連接,上傳的參數(shù)不會被存儲在內(nèi)存、硬盤和服務(wù)器中,返回的數(shù)據(jù)也不會被緩存在內(nèi)存或者硬盤中
  • 2.根據(jù)請求需要進(jìn)行到哪個層次進(jìn)行分類:
    • 1.to_memory:僅僅向內(nèi)存進(jìn)行數(shù)據(jù)請求。
    • 2.to_local:在向內(nèi)存請求數(shù)據(jù)無果之后,去硬盤中進(jìn)行數(shù)據(jù)請求
    • 3.to_network:在向內(nèi)存和硬盤請求數(shù)據(jù)無果之后,去服務(wù)器中進(jìn)行數(shù)據(jù)請求。

什么樣的數(shù)據(jù)更適合在本地進(jìn)行緩存呢?

  • 1.我們首先可以考慮服務(wù)器數(shù)據(jù)庫中不常更新的數(shù)據(jù)表,例如:國家、地區(qū)、機(jī)關(guān)的信息表等等,這種數(shù)據(jù)需要客戶端開發(fā)人員與后臺人員進(jìn)行溝通后確認(rèn)。我認(rèn)為像更改頻率在 1天/次以上 并且 用戶不可操作 的數(shù)據(jù)表就可以稱為不常更新數(shù)據(jù)表。
  • 2.僅僅關(guān)于個人的信息,例如:用戶個人信息、用戶聯(lián)系人表、用戶行為表等等。這里需要注意的一點是僅僅關(guān)于個人,因為像手Q發(fā)的動態(tài)和百度貼吧的帖子也是個人發(fā)的信息,那么這類數(shù)據(jù)適不適合進(jìn)行本地緩存呢?我的結(jié)論是 不適合 ,因為像這種個人數(shù)據(jù),摻雜著其他用戶的信息,一旦其他用戶和這種個人數(shù)據(jù)交互的時候,本人app中的數(shù)據(jù)就需要花很大的力氣進(jìn)行數(shù)據(jù)同步。(這里的例子不是很準(zhǔn)確,因為像手Q和貼吧都可以以推送的形式進(jìn)行數(shù)據(jù)同步并且送達(dá)率很高。而我們一般開發(fā)者使用的都是第三方的推送,一旦推送量比較大送達(dá)率很難接近50%,所以這種數(shù)據(jù)同步的方式不可取。)

前面我們說了兩類可以在本地進(jìn)行緩存的數(shù)據(jù)表,那么這兩類數(shù)據(jù)該如何與服務(wù)器同步?

  • 1.對于不常更新的數(shù)據(jù)表,我們可以選定兩三個除GET以外的用戶使用頻率不高的請求(例如:登錄接口等等),這些請求在使用的時候必然會連上服務(wù)器。我們可以在這些接口中附加<時間戳:數(shù)據(jù)表>的鍵值對列表字段,然后讓服務(wù)器對時間戳進(jìn)行判斷,判斷本地緩存的數(shù)據(jù)表中的數(shù)據(jù)是否被更新過,最后在接口中返回需要更新的數(shù)據(jù)表標(biāo)識,以便客戶端在下次在本地請求該過期表的時候跳過內(nèi)存和硬盤緩存直接去服務(wù)器取回相應(yīng)的更新數(shù)據(jù)并更新本地表。
  • 2.對于個人數(shù)據(jù),只要保證網(wǎng)絡(luò)請求成功的時候也在本地緩存中插入緩存,即可同步數(shù)據(jù)。
  • 3.其實還有一種方式就是在服務(wù)器更改數(shù)據(jù)之后,將數(shù)據(jù)推送給客戶端,但是我在比較了第三方推送的推送率之后選擇了放棄這個辦法,因為客戶端和服務(wù)器之間的數(shù)據(jù)完整性是比較重要的。

二、數(shù)據(jù)引擎的設(shè)計模式##

分析了上面的三個問題之后,我們得想想要使用什么樣的方法來設(shè)計我們的數(shù)據(jù)引擎,熟悉java設(shè)計模式的同學(xué)可能會想到很多東西。我最近看了 Android源碼設(shè)計模式解析與實戰(zhàn) 之后也了解到了許多設(shè)計模式,現(xiàn)在的問題是怎么用這些設(shè)計模式呢?還好我最近又翻譯了Fresco和OkHttp的源碼,所以可以從這兩個牛逼的開源項目中吸取一點經(jīng)驗。

  • 1.單例:這個我想絕大部分同學(xué)應(yīng)該沒問題,所以簡單講講:我們可以將 內(nèi)存緩存、硬盤緩存、Http請求服務(wù)、數(shù)據(jù)引擎 這些需要花費(fèi)大量資源創(chuàng)建的實例設(shè)置為單例。
  • 2.Builder模式:對于一些構(gòu)造參數(shù)比較多的類,我們可以使用這個模式來使代碼變得清晰;
  • 3.外觀模式:我們的數(shù)據(jù)引擎,只需要暴露出一個 數(shù)據(jù)引擎的實例就可以了,其他像 內(nèi)存緩存、硬盤緩存、Http請求服務(wù)等,都可以作為子系統(tǒng)集成在數(shù)據(jù)引擎實例中。
  • 4.靜態(tài)工廠模式:對于有多個構(gòu)造函數(shù)的類,我們可以使用這個模式,使客戶端在使用的時候不會傳錯參數(shù)。
  • 5.設(shè)計模式六大原則中的五點:
    • 1.單一職責(zé)原則:數(shù)據(jù)引擎中每一個子系統(tǒng)都只能關(guān)注自己的功能實現(xiàn),不參與其他功能的任何邏輯
    • 2.開閉原則:對于數(shù)據(jù)引擎,我們不能在其內(nèi)部持有各個子系統(tǒng)的具體實現(xiàn),在開發(fā)過程中我們可能會隨時遇見更好的子系統(tǒng)的實現(xiàn),為了方便更改實現(xiàn),我們需要在數(shù)據(jù)引擎中持有各個子功能抽象出來的接口,當(dāng)我們遇見更好的子系統(tǒng)的實現(xiàn)的時候只需要讓其實現(xiàn)該接口,可以隨時更換實現(xiàn)。
    • 3.里氏替換原則:在2的基礎(chǔ)上,任何子系統(tǒng)的實現(xiàn)都能直接放入數(shù)據(jù)引擎中,而不需要任何其他的配置,如if等條件。
    • 4.依賴倒置原則:各個子功能的實現(xiàn)類不需要有任何交流,交流都是通過子功能接口實現(xiàn)的。用職責(zé)鏈模式實現(xiàn)這個原則,后面會講到。
    • 5.接口隔離原則:每個子功能的接口需要最精簡。
  • 6.職責(zé)鏈模式:這里是借鑒OkHttp的攔截器。將 MemoryCache-》DiskCache-》NetWork 這樣的運(yùn)行流程以攔截器鏈的形式串起來。攔截器鏈中子功能的交流都是以接口的形式,這也就實現(xiàn)了 依賴倒置原則。
  • 7.緩存生命周期的log展示:這里是借鑒Fresco,內(nèi)存緩存和硬盤緩存 的緩存條目 從存在到刪除可以看成一個完整的生命周期。為了方便debug,我們可以將(插入、更新、刪除、清空等操作)看成一個個事件,然后建立一個監(jiān)聽器監(jiān)聽整個生命周期。
  • 8.享元模式:由于我們請求和緩存可能會比較頻繁,所以我們可以以對象池的形式復(fù)用對象,以減少大量創(chuàng)建和銷毀對象所需要的時間。

三、具體代碼實現(xiàn)##

務(wù)虛了這么久,也應(yīng)該務(wù)實了。先上項目例子數(shù)據(jù)引擎架構(gòu)項目源碼,建議大家下載過來,然后結(jié)合博客一起觀看下面我來分析一下數(shù)據(jù)引擎架構(gòu)的實現(xiàn)代碼。

1.請求和返回類設(shè)計

我們可以將所有的數(shù)據(jù)請求,當(dāng)成類似于網(wǎng)絡(luò)請求的格式。這樣一來我們可以創(chuàng)建兩個類一個Request,一個Response供客戶端使用

public class Request {

private static final String TAG="Request";
private static final Object RECYCLER_LOCK = new Object();
private static int MAX_RECYCLED = 5;
private static Request sFirstRecycledRequest;
private static int sRecycledCount;

private Request mNextRecycledRequest;

@NonNull
private RequestFlag mRequestFlag;
@NonNull
private CacheKey mCacheKey;
@Nullable
private Object mParam;
private boolean isCacheToMemory=true;
private boolean isSaveToLocal=true;
private boolean[] interceptorIsEnable=new boolean[]{true,true,true,true,true,true,true,true,true};

public static Request setFlag(@NonNull RequestFlag requestFlag){
    return obtain(requestFlag,null,null,null,-1);
}

public static Request setFlagParam(@NonNull RequestFlag requestFlag, @NonNull Object param){
    return obtain(requestFlag,param,null,null,-1);
}

public static Request setFlagParamKey(@NonNull RequestFlag requestFlag, @NonNull Object param, @NonNull String key){
    return obtain(requestFlag,param,key,null,-1);
}

public static Request setFlagParamInterceptorIsEnable(@NonNull RequestFlag requestFlag, @NonNull Object param, @NonNull boolean[] interceptorIsEnable){
    return obtain(requestFlag,param,null,interceptorIsEnable,-1);
}

public static Request setFlagParamWhichServiceUnable(@NonNull RequestFlag requestFlag, @NonNull Object param, int whichUnable){
    return obtain(requestFlag,param,null,null,whichUnable);
}

public void recycle() {
    synchronized (RECYCLER_LOCK) {
        if (sRecycledCount < MAX_RECYCLED) {
            reset();
            sRecycledCount++;

            if (sFirstRecycledRequest != null) {
                mNextRecycledRequest= sFirstRecycledRequest;
            }
            FLog.v(TAG,"回收Request  sRecycledCount:"+sRecycledCount);
            sFirstRecycledRequest= this;
        }
    }
}

private static Request obtain(@NonNull RequestFlag requestFlag, Object param, String key, boolean[] interceptorIsEnable, int whichUnable) {
    synchronized (RECYCLER_LOCK) {
        if (sFirstRecycledRequest!= null) {
            Request requestToReuse = sFirstRecycledRequest;
            sFirstRecycledRequest= requestToReuse.mNextRecycledRequest;
            requestToReuse.mNextRecycledRequest= null;

            requestToReuse.mRequestFlag=requestFlag;
            if (param==null){
                requestToReuse.mCacheKey=new SimpleCacheKey(requestFlag.toString());
            }else {
                requestToReuse.mParam=param;
                if (key!=null){
                    requestToReuse.mCacheKey = new SimpleCacheKey(key);
                }else {
                    requestToReuse.mCacheKey=new SimpleCacheKey(JsonUtil.objectToJson(param));
                    if (interceptorIsEnable!=null) {
                        requestToReuse.interceptorIsEnable = interceptorIsEnable;
                    }else {
                        if (whichUnable!=-1)requestToReuse.interceptorIsEnable[whichUnable]=false;
                    }
                }
            }
            sRecycledCount--;
            FLog.v(TAG,"從對象池中獲取Request  sRecycledCount:"+sRecycledCount);
            return requestToReuse;
        }
    }
    FLog.v(TAG,"對象池已空,創(chuàng)建一個Request  sRecycledCount:"+sRecycledCount);
    if (param==null){
        return new Request(requestFlag);
    }else {
        if (key!=null){
            return new Request(requestFlag,param,key);
        }else {
            if (interceptorIsEnable!=null) {
                return new Request(requestFlag,param,interceptorIsEnable);
            }else {
                if (whichUnable==-1){
                    return new Request(requestFlag,param);
                }else {
                    return new Request(requestFlag,param,whichUnable);
                }
            }
        }
    }
}

private void reset() {
    mRequestFlag=null;
    mCacheKey=null;
    mParam=null;
    isCacheToMemory=true;
    isSaveToLocal=true;
    interceptorIsEnable=new boolean[]{true,true,true,true,true,true,true,true,true};
}

private Request() {
}

private Request(@NonNull RequestFlag requestFlag) {
    mRequestFlag = requestFlag;
    mCacheKey=new SimpleCacheKey(mRequestFlag.toString());
}

private Request(@NonNull RequestFlag requestFlag, @Nullable Object param) {
    mRequestFlag = requestFlag;
    mParam = param;
    mCacheKey=new SimpleCacheKey(JsonUtil.objectToJson(param));
}

private Request(@NonNull RequestFlag requestFlag, @Nullable Object param, boolean[] interceptorIsEnable) {
    mRequestFlag = requestFlag;
    mParam = param;
    this.interceptorIsEnable = interceptorIsEnable;
    mCacheKey=new SimpleCacheKey(JsonUtil.objectToJson(param));
}

private Request(@NonNull RequestFlag requestFlag, @Nullable Object param, int whichUnable) {
    mRequestFlag = requestFlag;
    mParam = param;
    interceptorIsEnable[whichUnable]=false;
    mCacheKey=new SimpleCacheKey(JsonUtil.objectToJson(param));
}

private Request(@NonNull RequestFlag requestFlag, @Nullable Object param, String key) {
    mCacheKey = new SimpleCacheKey(key);
    mRequestFlag = requestFlag;
    mParam = param;
}

public Request setParam(@Nullable Object param) {
    this.mParam = param;
    return this;
}

public Request setRequestFlag(@NonNull RequestFlag requestFlag) {
    mRequestFlag = requestFlag;
    return this;
}

public Request setServiceIsEnable(int serviceNum, boolean isEnable) {
    if (serviceNum<interceptorIsEnable.length)interceptorIsEnable[serviceNum]=isEnable;
    return this;
}

public Request setInterceptorIsEnable(boolean[] interceptorIsEnable) {
    this.interceptorIsEnable = interceptorIsEnable;
    return this;
}

public boolean getWhichServiceIsEnable(int serviceNum) {
    return serviceNum < interceptorIsEnable.length && interceptorIsEnable[serviceNum];
}

public Request setCacheKey(@NonNull CacheKey cacheKey) {
    mCacheKey = cacheKey;
    return this;
}

public Request setCacheToMemory(boolean cacheToMemory) {
    isCacheToMemory = cacheToMemory;
    return this;
}

public Request setSaveToLocal(boolean saveToLocal) {
    isSaveToLocal = saveToLocal;
    return this;
}

public boolean[] getInterceptorIsEnable() {
    return interceptorIsEnable;
}

@Nullable
public Object getParam() {
    return mParam;
}

@NonNull
public RequestFlag getRequestFlag() {
    return mRequestFlag;
}

@NonNull
public CacheKey getCacheKey() {
    return mCacheKey;
}

public boolean isCacheToMemory() {
    return isCacheToMemory;
}

public boolean isSaveToLocal() {
    return isSaveToLocal;
}

@Override
public String toString() {
    return "Request:{" +
            "mCacheKey=" + mCacheKey +
            ", mRequestFlag=" + mRequestFlag +
            ", mParam=" + mParam +
            ", isCacheToMemory=" + isCacheToMemory +
            ", isSaveToLocal=" + isSaveToLocal +
            ", interceptorIsEnable=" + Arrays.toString(interceptorIsEnable) +
            '}';
}
}
  • 1.Request:我們在考慮請求的時候我總結(jié)了以下幾個在請求鏈之中需要用到的東西
    • 1.Object mParam:請求的參數(shù),由于各種數(shù)據(jù)請求需要的參數(shù)是不同的,所以我們可以將請求參數(shù)設(shè)置為Object,在具體請求中進(jìn)行更改
    • 2.CacheKey mCacheKey:CacheKey是一個接口,這個接口的實現(xiàn)類會作為內(nèi)存和硬盤緩存時候的key來使用
    • 3.RequestFlag mRequestFlag:每一個不同的請求我們都需要用一個enum類來標(biāo)識。RequestFlag是一個接口,我們根據(jù)前面第一個請求的分類(NONE、GET、INSERT),實現(xiàn)了三個不同的enum,然后讓這三個enum實現(xiàn)RequestFlag。這樣一來就能用一個RequestFlag,標(biāo)識出一個請求的種類,一個RequestFlag中除了有請求的分類,里面還有請求的層次(to_memory、to_local、to_network)。
    • 4.boolean isCacheToMemory:在請求從硬盤或者網(wǎng)絡(luò)返回的時候,我們需要判斷該請求返回的數(shù)據(jù)是否需要被內(nèi)存緩存,默認(rèn)是需要的
    • 5.boolean isSaveToLocal:在請求從網(wǎng)絡(luò)返回的時候,我們需要判斷該請求返回的數(shù)據(jù)是否需要被存儲在本地,默認(rèn)是需要的
    • 6.boolean[] interceptorIsEnable:在請求鏈之中,我們可能需要屏蔽某些攔截器,此時可以在這里設(shè)置哪些攔截器奏效,注意這里的攔截是指請求上傳時候的攔截。
    • 7.除上面幾個參數(shù),其他的參數(shù)都是為了創(chuàng)建對象池構(gòu)建的,這里的對象池使用了鏈表的方式,在使用obtain方法獲取的時候,將鏈?zhǔn)椎膶ο笕〕?。在使用recycle方法回收本對象的時候,將本對象設(shè)為鏈?zhǔn)?。鏈表設(shè)置了最大值,當(dāng)超過最大值的時候,就直接使用創(chuàng)建的對象
    • 8.在獲取Reuqest對象的時候,使用了由于要傳入的參數(shù)比較多,所以使用了靜態(tài)工廠模式和類似Builder的鏈?zhǔn)絼?chuàng)建模式

public class Response<Response1,Response2 ,Response3> {

private boolean isSuccess;
private Exception mException;

private Response1 mResponse1;
private Response2 mResponse2;
private Response3 mResponse3;

public static <T1,T2,T3> Response<T1,T2,T3>  getFailedResponse(Exception exception){
    return new Response<T1,T2,T3>(false,exception);
}

public static Response getCommonResponseOne(Object response1){
    return new Response<Object,Object,Object>(response1);
}

public static Response getCommonResponseTwo(Object response1,Object response2){
    return new Response<Object,Object,Object>(response1,response2);
}

public static Response getCommonResponseThree(Object response1,Object response2,Object response3){
    return new Response<Object,Object,Object>(response1,response2,response3);
}

public static <T> Response getResponseOne(T response1){
    return new Response<T,Object,Object>(response1);
}

public static <T1,T2> Response getResponseTwo(T1 response1,T2 response2){
    return new Response<T1,T2,Object>(response1);
}

public static <T1,T2,T3> Response getResponseOne(T1 response1,T2 response2,T3 response3){
    return new Response<T1,T2,T3>(response1,response2,response3);
}

private Response(boolean isSuccess, Exception exception) {
    this.isSuccess = isSuccess;
    mException = exception;
}

private Response(Response1 response1) {
    mResponse1 = response1;
    isSuccess=true;
}

private Response(Response1 response1, Response2 response2) {
    mResponse1 = response1;
    mResponse2 = response2;
    isSuccess=true;
}

private Response(Response1 response1, Response2 response2, Response3 response3) {
    mResponse1 = response1;
    mResponse2 = response2;
    mResponse3 = response3;
    isSuccess=true;
}

public Response1 getResponse1() {
    return mResponse1;
}

public void setResponse1(Response1 response1) {
    mResponse1 = response1;
}

public Response2 getResponse2() {
    return mResponse2;
}

public void setResponse2(Response2 response2) {
    mResponse2 = response2;
}

public Response3 getResponse3() {
    return mResponse3;
}

public void setResponse3(Response3 response3) {
    mResponse3 = response3;
}

public boolean isSuccess() {
    return isSuccess;
}

public Exception getException() {
    return mException;
}
}
  • 2.Response:這個類比較簡單,因為我們在客戶端使用的時候,可能會將一個數(shù)據(jù)結(jié)果轉(zhuǎn)化為多個實體供界面使用,所以有三個泛型參數(shù)可以設(shè)置。還有就是請求出錯的時候返回的Exception,注意這里的出錯僅僅指的是異常,其他類似于數(shù)據(jù)不符合的情況不在考慮之中。

2.攔截鏈的骨架

我們在前面的設(shè)計模式中提到了,在設(shè)計一個架構(gòu)的時候需要面向接口編程,這樣才會符合前面說的設(shè)計模式六大原則。

public interface Interceptor {
Object intercept(Chain chain,boolean enable) throws Exception;

Service getService();
interface Chain {
    Object proceed() throws Exception;

    Request getRequest();
}
}   

public interface Service {
boolean isEnabled();

void setEnable(boolean enable);

Object in(Request request, Object in) throws Exception;

Object out(Request request) throws Exception;
}
  • 1.Interceptor接口:我們前面提到的攔截器鏈,就是由一個個攔截器組成,而每個攔截器會實現(xiàn)Interceptor接口。####
    • 1.intercept方法就是每個攔截器要做的事情,在各個具體的攔截器中實現(xiàn)
    • 2.Chain其實就是整個攔截鏈的上下文,這個接口的實現(xiàn)會持有 攔截器鏈、Request和當(dāng)前攔截器的編號。就相當(dāng)于Chain負(fù)責(zé)調(diào)用了處理每個Interceptor的調(diào)用和返回
  • 2.Service接口:我們不可能在Interceptor的intercept實現(xiàn)所有的邏輯,所以我們需要將攔截器中需要的操作封裝成一個個的服務(wù),這樣能有效減小耦合。而每個Interceptor中持有的都只是Service接口,不會持有具體的實現(xiàn)。
    • 1.前面我們說了,在某些情況下需要將攔截器設(shè)置為是否可用,前兩個方法就是這一個用處
    • 2.in方法表示要將參數(shù)in放入這個服務(wù)之中,典型的例子就是將返回的數(shù)據(jù)放入內(nèi)存緩存和硬盤緩存中
    • 3.out方法表示要從服務(wù)中返回數(shù)據(jù),典型的例子就是從內(nèi)存、硬盤、內(nèi)存緩存中返回數(shù)據(jù)

3.攔截器鏈和攔截器的實現(xiàn)

我實現(xiàn)了四個攔截器:內(nèi)存緩存攔截器-->新線程攔截器-->硬盤儲存攔截器-->網(wǎng)絡(luò)請求攔截器。和一個攔截器鏈,這個鏈中使用了上面四個攔截器。

public class RealInterceptorChain implements Interceptor.Chain {

public static String TAG="RealInterceptorChain";
private static final Object RECYCLER_LOCK = new Object();
private static int MAX_RECYCLED = 5;
private static RealInterceptorChain sFirstRecycledChain;
private static int sRecycledCount;

private RealInterceptorChain mNextRecycledChain;

private Request mRequest;
private final List<Interceptor> interceptors;
private int index=0;

private RealInterceptorChain(Request request) {
    mRequest=request;
    this.interceptors = DataEngine.mInterceptors;
}

@Override
public Object proceed() throws Exception {
    if (index >= interceptors.size()){
        throw new AssertionError();
    }
    Interceptor interceptor = interceptors.get(index);
    boolean isEnable=mRequest.getInterceptorIsEnable()[index];
    index++;
    return interceptor.intercept(this,isEnable);
}

@Override
public Request getRequest() {
    return mRequest;
}

public void recycle() {
    synchronized (RECYCLER_LOCK) {
        if (sRecycledCount < MAX_RECYCLED) {
            reset();
            sRecycledCount++;

            if (sFirstRecycledChain != null) {
                mNextRecycledChain = sFirstRecycledChain;
            }
            sFirstRecycledChain = this;
            FLog.v(TAG,"回收Chain  sRecycledCount:"+sRecycledCount);
        }
    }
}

public static RealInterceptorChain obtain(Request request) {
    synchronized (RECYCLER_LOCK) {
        if (sFirstRecycledChain != null) {
            RealInterceptorChain eventToReuse = sFirstRecycledChain;
            sFirstRecycledChain = eventToReuse.mNextRecycledChain;
            eventToReuse.mNextRecycledChain = null;
            eventToReuse.mRequest=request;
            sRecycledCount--;
            FLog.v(TAG,"從對象池中獲取Chain  sRecycledCount:"+sRecycledCount);
            return eventToReuse;
        }
    }
    FLog.v(TAG,"對象池已空,創(chuàng)建一個Chain  sRecycledCount:"+sRecycledCount);
    return new RealInterceptorChain(request);
}

private void reset() {
    mRequest = null;
    index=0;
}
}
  • 1.RealInterceptorChain:實現(xiàn)了前面說的Chain,有以下特點
    • 1.Request mRequest:該鏈的請求,用于所有的攔截器
    • 2.List<Interceptor> interceptors:攔截器鏈,存有所有攔截器
    • 3.int index:用于標(biāo)記請求已經(jīng)運(yùn)行到了哪一個攔截器中
    • 4.其他的參數(shù)和Request類似,都是為構(gòu)造對象池而設(shè)計
    • 5.proceed()方法:在請求開始的時候,這個方法會被調(diào)用。在獲取了攔截器1之后,將index++,會調(diào)用攔截器1的intercept方法,并傳入本Chain對象和該Reuqest中該攔截器是否起作用的標(biāo)志isEnable。在攔截器1中會根據(jù)情況,適時調(diào)用本Chain對象的proceed()方法,這時又回到了這個方法,但是使用的攔截器變了。像這樣調(diào)用完了所有的攔截器之后會按層次返回并在相應(yīng)的攔截器中對返回的參數(shù)進(jìn)行處理。

@Immutable

public class MemoryCacheInterceptor implements Interceptor {

public static String TAG="MemoryCacheInterceptor";
private final Service memoryCacheService;

public MemoryCacheInterceptor(Service memoryCacheService) {
    this.memoryCacheService = memoryCacheService;
}

@Override
public Object intercept(final Chain chain, boolean enable) throws Exception {
    final Request request=chain.getRequest();
    Object response = null;
    boolean isEnable=enable&&getService().isEnabled();
    RequestFlag requestFlag= request.getRequestFlag();
    FLog.v(TAG,request.getRequestFlag().toString()+"請求進(jìn)入");

    if (requestFlag.getRequestLevel()==RequestLevel.to_memory){
        //在這種情況下,如果不開啟內(nèi)存緩存,那么就直接返回null
        if (!isEnable) response=null;
        switch (requestFlag.getRequestType()){
            case GET:
                //返回內(nèi)存緩存中的結(jié)果
                response= memoryCacheService.out(request);
                break;
            case INSERT:
                //在更新內(nèi)存緩存之后,返回更新后的結(jié)果
                response= memoryCacheService.in(request,null);
                break;
            case NONE:
                //在memory下,不會有這種請求
                response= null;
                break;
        }
    }else {
        boolean isNeedToNextService=true;
        switch (requestFlag.getRequestType()){
            case GET:
                if (isEnable){
                    //內(nèi)存緩存服務(wù)可以使用,就使用他
                    response= memoryCacheService.out(request);
                    //如果從內(nèi)存緩存中獲取的結(jié)果不為null,那么就不需要去下一個服務(wù)取數(shù)據(jù)
                    if (response!=null)isNeedToNextService=false;
                }
                //這里不用 break 可以直接在需要進(jìn)入下一個服務(wù)的情況下,復(fù)用代碼。
            case INSERT:
                // 如果請求是GET,表示從內(nèi)存中獲取的結(jié)果是null,所以需要進(jìn)入下一個服務(wù)獲取數(shù)據(jù)
                // 如果請求是INSERT 由于這種請求肯定要經(jīng)過下一個服務(wù),所以不需要判斷本服務(wù)是否可用
                // 下一個服務(wù)開始要去本地或者網(wǎng)絡(luò)獲取數(shù)據(jù)了,所以返回的是 Observable
                if (isNeedToNextService)
                    response= ((Observable<?>)chain.proceed())
                            .map(new Func1<Object, Object>() {
                            @Override
                            public Object call(Object o) {
                                //如果回調(diào)后的結(jié)果不是null并且允許該數(shù)據(jù)被緩存(默認(rèn)是允許的,除非在下一個服務(wù)返回的時候禁止了),就緩存這個結(jié)果
                                if (o != null && request.isCacheToMemory())try {
                                    memoryCacheService.in(request, o);
                                } catch (Exception e) {
                                    //此時是 內(nèi)存緩存的存儲出了問題
                                    FLog.e(TAG, "內(nèi)存緩存的存儲出了問題", e);
                                    return Response.getFailedResponse(e);
                                }
                                return o;
                            }
                        });
                break;
            case NONE:
                //這種請求直接進(jìn)入下一個服務(wù),下一個服務(wù)開始要去本地或者網(wǎng)絡(luò)獲取數(shù)據(jù)了,所以返回的是 Observable
                response= chain.proceed();
                break;
        }
    }
    FLog.v(TAG,request.getRequestFlag().toString()+"  "+TAG+"返回");
    return response;
}

@Override
public Service getService() {
    return memoryCacheService;
}

}

  • 2.MemoryCacheInterceptor是第一個攔截器,當(dāng)調(diào)用RealInterceptorChain#proceed()的時候這個攔截器的intercept()會被調(diào)用。
    • 1.看代碼可知,整個MemoryCacheInterceptor都是使用Service來進(jìn)行具體功能的實現(xiàn),并沒有和具體的實現(xiàn)類耦合,這么以來以后進(jìn)行拓展或者更改Service的實現(xiàn)就會比較方便。
    • 2.整個方法中在開頭創(chuàng)建了幾個local對象,request和response不用多說,isEnable是一個標(biāo)記該攔截器是否奏效的flag。只有當(dāng)Request中的flag和該Service的flag同時奏效,這個攔截器才會被奏效。其實也很好理解,Service的flag就相當(dāng)于總開關(guān),Request的flag就相當(dāng)于臨時開關(guān),只有總開關(guān)和臨時開關(guān)都開啟的時候這個Service才會被使用。
    • 3.RequestFlag是一個很重要的對象,我們前面說了這個類中包含了RequestType和RequestLevel,這些都會在接下來的分派中使用到。
    • 4.接下來會根據(jù)RequestFlag中的RequestLevel進(jìn)行一個選擇,如果這里的RequestLevel是to_memory的話,那說明這個請求僅僅需要向內(nèi)存中獲取數(shù)據(jù),判斷完成了之后再根據(jù)RequestType進(jìn)行插入或獲取數(shù)據(jù)。
    • 5.如果RequestFlag不是to_memory,那么這個請求就有進(jìn)入下一個攔截器的可能。
      • 1.GET:如果不是to_memory,那么這個請求會先去內(nèi)存中取數(shù)據(jù),如果內(nèi)存中沒有數(shù)據(jù),就要進(jìn)入其他線程去本地或者網(wǎng)絡(luò)取數(shù)據(jù)了
      • 2.INSERT:如果不是to_memory,又由于是插入,所以肯定需要將數(shù)據(jù)插入到本地或者服務(wù)器中,所以使用了RxJava的線程切換,返回的是一個Observable,在這個Observable的回調(diào)中,有些插入操作可能會返回一些數(shù)據(jù),此時就要根據(jù)是否有返回數(shù)據(jù)和Request中的是否需要在內(nèi)存中緩存數(shù)據(jù)來判斷是否需要進(jìn)行內(nèi)存緩存。Request#isCacheToMemory()這個參數(shù)可以在本地數(shù)據(jù)攔截器或者網(wǎng)絡(luò)攔截器中被修改,所以返回的是否需要內(nèi)存緩存,就看具體情況了。
      • 3.NONE:這種請求是必須去網(wǎng)絡(luò)中進(jìn)行驗證的,所以既不需要內(nèi)存緩存也不需要在從內(nèi)存中獲取數(shù)據(jù)。由于需要切換線程,所以返回的也是Observable。
    • 6.最后就是返回Object response,這個response可能是一般的數(shù)據(jù)類,也可能是Observable,所以在調(diào)用處需要進(jìn)行判斷,這在后面會講解到

@Immutable

public class NewThreadInterceptor implements Interceptor {
public static String TAG="NewThreadInterceptor";

@Override
public Observable<Object> intercept(final Chain chain, boolean enable)  {
    return Observable.just(chain)
            .observeOn(Schedulers.io())
            .map(new Func1<Chain, Object>() {
                @Override
                public Object call(Chain chain) {
                    try {
                        return chain.proceed();
                    } catch (Exception e) {
                        //此時是本地存儲或者網(wǎng)絡(luò)請求出了問題
                        FLog.e(TAG,"本地存儲或者網(wǎng)絡(luò)請求出了問題",e);
                        return Response.getFailedResponse(e);
                    }
                }
            });
}

@Override
public Service getService() {
    return null;
}
}
  • 3.NewThreadInterceptor是第二個攔截器,用于適配Rxjava,會返回一個Observable,在線程切換了之后會調(diào)用Chain#proceed()觸發(fā)下一個攔截器的intercept()。

@Immutable

public class LocalDataInterceptor implements Interceptor {
public static String TAG="LocalDataInterceptor";
private final Service localDataService;

public LocalDataInterceptor(Service localDataService) {
    this.localDataService = localDataService;
}

@Override
public Object intercept(Chain chain,boolean enable) throws Exception {
    final Request request=chain.getRequest();
    Object response = null;
    boolean isEnable=enable&&getService().isEnabled();
    RequestFlag requestFlag= request.getRequestFlag();
    FLog.v(TAG,request.getRequestFlag().toString()+"請求進(jìn)入");

    if (requestFlag.getRequestLevel()==RequestLevel.to_local){
        //在這種情況下,如果不開啟本地數(shù)據(jù)服務(wù),那么就直接返回null
        if (!isEnable)return null;
        switch (requestFlag.getRequestType()){
            case GET:
                //返回本地數(shù)據(jù)中的結(jié)果
                response= localDataService.out(request);
                break;
            case INSERT:
                //在更新內(nèi)存緩存之后,返回更新后的結(jié)果
                response= localDataService.in(request,null);
                break;
            case NONE:
                //根據(jù)傳入的信息和內(nèi)存緩存中的信息,經(jīng)過驗證操作后,驗證返回結(jié)果
                response= localDataService.in(request,null);
                break;
        }
    }else {
        boolean isNeedToNextService=true;
        switch (requestFlag.getRequestType()){
            case GET:
                if (isEnable) {
                    //本地存儲服務(wù)可以使用,就使用他
                    response = localDataService.out(request);
                    //如果從本地儲存中獲取的結(jié)果不為null,那么就不需要去下一個服務(wù)取數(shù)據(jù)
                    if (response != null) isNeedToNextService = false;
                    //這里不用 break 可以直接在需要進(jìn)入下一個服務(wù)的情況下,復(fù)用代碼。
                }
            case INSERT:
                // 如果請求是GET,表示從本地獲取的結(jié)果是null,所以需要進(jìn)入下一個服務(wù)獲取數(shù)據(jù)
                // 如果請求是INSERT 由于這種請求肯定要經(jīng)過下一個服務(wù),所以不需要判斷本服務(wù)是否可用
                if (isNeedToNextService){
                    response= chain.proceed();
                    //如果下一個服務(wù)取來的結(jié)果不是null,并且這個數(shù)據(jù)被允許存在本地(默認(rèn)是允許的,除非在下一個服務(wù)返回的時候禁止了),,就把這個結(jié)果存起來
                    if (response!=null&&request.isSaveToLocal()) localDataService.in(request,response);
                }
                break;
            case NONE:
                //由于這種請求肯定要經(jīng)過下一個服務(wù),所以不需要判斷本服務(wù)是否可用,也不需要將返回的結(jié)果存在本地
                response= chain.proceed();
                break;
        }
    }
    FLog.v(TAG,request.getRequestFlag()+"  "+TAG+"返回");
    return response;
}

@Override
public Service getService() {
    return localDataService;
}
}
  • 3.第三個攔截器是LocalDataInterceptor,這個攔截器提供本地儲存服務(wù),具體實現(xiàn)與請求分派方式和MemoryCacheInterceptor類似,不過也有幾個不同點
    • 1.在to_local的情況下NONE是有用處的。
    • 2.由于不需要像MemoryCacheInterceptor一樣切換線程,所以這里的調(diào)用都是同步返回,返回的也都是數(shù)據(jù)類。

@Immutable

public class NetworkInterceptor implements Interceptor{
public static String TAG="NetworkInterceptor";

private final Service mNetworkService;

public NetworkInterceptor(Service networkService) {
    this.mNetworkService = networkService;
}

@Override
public Object intercept(Chain chain,boolean enable) throws Exception {
    final Request request=chain.getRequest();
    boolean isEnable=enable&&getService().isEnabled();
    FLog.v(TAG,request.getRequestFlag().toString()+"請求進(jìn)入");
    //如果網(wǎng)絡(luò)服務(wù)不可用,就返回null
    if (!isEnable)return null;
    Object response=mNetworkService.out(chain.getRequest());
    FLog.v(TAG,request.getRequestFlag().toString()+"  "+TAG+"返回");
    return response;
}

@Override
public Service getService() {
    return mNetworkService;
}
}
  • 4.第四個攔截器是NetworkInterceptor,這個就是網(wǎng)絡(luò)請求的攔截器了,由于網(wǎng)絡(luò)請求服務(wù)和本地儲存服務(wù)、內(nèi)存服務(wù)不同,所以在網(wǎng)絡(luò)服務(wù)可用的情況下,只會調(diào)用Service#out(),而Service#in()不會被調(diào)用。

整個攔截器鏈的實現(xiàn)就是上面這樣,可以看出雖然我并沒有講解各個攔截器中的服務(wù)具體是怎么實現(xiàn)的,但是這并不影響整個攔截器鏈的邏輯。由于我們定義了Service這個抽象的接口,我們在攔截器鏈的實現(xiàn)過程中,并不需要去在意Service的具體邏輯,這就是將攔截器和服務(wù)解耦,而一旦解耦了,Service的實現(xiàn)類中無論如何變化,都影響不到整個攔截器鏈的框架。

由于字?jǐn)?shù)太多:所以分成了兩篇:Android數(shù)據(jù)層架構(gòu)的實現(xiàn) 下篇

不販賣焦慮,也不標(biāo)題黨。分享一些這個世界上有意思的事情。題材包括且不限于:科幻、科學(xué)、科技、互聯(lián)網(wǎng)、程序員、計算機(jī)編程。下面是我的微信公眾號:世界上有意思的事,干貨多多等你來看。

世界上有意思的事

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

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,030評論 25 709
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評論 19 139
  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,798評論 11 349
  • 這幾天西餐廳離職的員工還蠻多的,所以也算是有感而發(fā),當(dāng)然也算是完成新聞組這個月的任務(wù),與本部門的諸君共勉。 我還記...
    pudding閱讀 848評論 0 2
  • 【作者】郭興平 【派別】文魁派 【導(dǎo)師】袁文魁 【導(dǎo)圖講解】 中心圖是帶有月餅字樣及圖像的圖片,直接點明中心主題。...
    郭興平閱讀 2,840評論 3 0

友情鏈接更多精彩內(nèi)容