天天看點

Android資料層架構的實作 上篇

最近我們app的伺服器吃不消了,是以我在為伺服器增加緩存層之後,又想到在app端進行二級緩存以減少app對伺服器的通路。我想很多app應該在項目的初期架構的時候就考慮到了這個問題,但是我當時開發這個app的時候完全不懂架構和設計模式,是以對性能根本沒有考慮,導緻了現在伺服器經常崩潰。關于伺服器的優化之後有時間再說,今天我們先來看看如何對一個app的資料的請求和緩存進行架構。

一.資料請求的分類、資料的可緩存性分析和資料同步##

如何對請求進行歸類?這是個問題,在Http請求裡設定了八種請求的方式,但是顯然并不适合我們這裡的情況。我對我們app中的資料請求進行歸類之後進行了一下兩種分類
  • 1.根據需要對請求做那些操作進行分類:
    • 1.GET:需要去記憶體、硬碟和伺服器中取資料的請求,請求時可以提供參數,但是參數并不會上傳到記憶體、硬碟和伺服器中。
    • 2.INSERT:需要将資料上傳至記憶體、硬碟和伺服器中,傳回的資料根據具體情況判斷是否需要緩存到記憶體或者硬碟中。
    • 3.NONE:向伺服器進行驗證操作,必須和伺服器連接配接,上傳的參數不會被存儲在記憶體、硬碟和伺服器中,傳回的資料也不會被緩存在記憶體或者硬碟中
  • 2.根據請求需要進行到哪個層次進行分類:
    • 1.to_memory:僅僅向記憶體進行資料請求。
    • 2.to_local:在向記憶體請求資料無果之後,去硬碟中進行資料請求
    • 3.to_network:在向記憶體和硬碟請求資料無果之後,去伺服器中進行資料請求。
什麼樣的資料更适合在本地進行緩存呢?
  • 1.我們首先可以考慮伺服器資料庫中不常更新的資料表,例如:國家、地區、機關的資訊表等等,這種資料需要用戶端開發人員與背景人員進行溝通後确認。我認為像更改頻率在 1天/次以上 并且 使用者不可操作 的資料表就可以稱為不常更新資料表。
  • 2.僅僅關于個人的資訊,例如:使用者個人資訊、使用者聯系人表、使用者行為表等等。這裡需要注意的一點是僅僅關于個人,因為像手Q發的動态和百度貼吧的文章也是個人發的資訊,那麼這類資料适不适合進行本地緩存呢?我的結論是 不适合 ,因為像這種個人資料,摻雜着其他使用者的資訊,一旦其他使用者和這種個人資料互動的時候,本人app中的資料就需要花很大的力氣進行資料同步。(這裡的例子不是很準确,因為像手Q和貼吧都可以以推送的形式進行資料同步并且送達率很高。而我們一般開發者使用的都是第三方的推送,一旦推送量比較大送達率很難接近50%,是以這種資料同步的方式不可取。)
前面我們說了兩類可以在本地進行緩存的資料表,那麼這兩類資料該如何與伺服器同步?
  • 1.對于不常更新的資料表,我們可以標明兩三個除GET以外的使用者使用頻率不高的請求(例如:登入接口等等),這些請求在使用的時候必然會連上伺服器。我們可以在這些接口中附加<時間戳:資料表>的鍵值對清單字段,然後讓伺服器對時間戳進行判斷,判斷本地緩存的資料表中的資料是否被更新過,最後在接口中傳回需要更新的資料表辨別,以便用戶端在下次在本地請求該過期表的時候跳過記憶體和硬碟緩存直接去伺服器取回相應的更新資料并更新本地表。
  • 2.對于個人資料,隻要保證網絡請求成功的時候也在本地緩存中插入緩存,即可同步資料。
  • 3.其實還有一種方式就是在伺服器更改資料之後,将資料推送給用戶端,但是我在比較了第三方推送的推送率之後選擇了放棄這個辦法,因為用戶端和伺服器之間的資料完整性是比較重要的。

二、資料引擎的設計模式##

分析了上面的三個問題之後,我們得想想要使用什麼樣的方法來設計我們的資料引擎,熟悉java設計模式的同學可能會想到很多東西。我最近看了 Android源碼設計模式解析與實戰 之後也了解到了許多設計模式,現在的問題是怎麼用這些設計模式呢?還好我最近又翻譯了Fresco和OkHttp的源碼,是以可以從這兩個牛逼的開源項目中吸取一點經驗。
  • 1.單例:這個我想絕大部分同學應該沒問題,是以簡單講講:我們可以将 記憶體緩存、硬碟緩存、Http請求服務、資料引擎 這些需要花費大量資源建立的執行個體設定為單例。
  • 2.Builder模式:對于一些構造參數比較多的類,我們可以使用這個模式來使代碼變得清晰;
  • 3.外觀模式:我們的資料引擎,隻需要暴露出一個 資料引擎的執行個體就可以了,其他像 記憶體緩存、硬碟緩存、Http請求服務等,都可以作為子系統內建在資料引擎執行個體中。
  • 4.靜态工廠模式:對于有多個構造函數的類,我們可以使用這個模式,使用戶端在使用的時候不會傳錯參數。
  • 5.設計模式六大原則中的五點:
    • 1.單一職責原則:資料引擎中每一個子系統都隻能關注自己的功能實作,不參與其他功能的任何邏輯
    • 2.開閉原則:對于資料引擎,我們不能在其内部持有各個子系統的具體實作,在開發過程中我們可能會随時遇見更好的子系統的實作,為了友善更改實作,我們需要在資料引擎中持有各個子功能抽象出來的接口,當我們遇見更好的子系統的實作的時候隻需要讓其實作該接口,可以随時更換實作。
    • 3.裡氏替換原則:在2的基礎上,任何子系統的實作都能直接放入資料引擎中,而不需要任何其他的配置,如if等條件。
    • 4.依賴倒置原則:各個子功能的實作類不需要有任何交流,交流都是通過子功能接口實作的。用職責鍊模式實作這個原則,後面會講到。
    • 5.接口隔離原則:每個子功能的接口需要最精簡。
  • 6.職責鍊模式:這裡是借鑒OkHttp的攔截器。将 MemoryCache-》DiskCache-》NetWork 這樣的運作流程以攔截器鍊的形式串起來。攔截器鍊中子功能的交流都是以接口的形式,這也就實作了 依賴倒置原則。
  • 7.緩存生命周期的log展示:這裡是借鑒Fresco,記憶體緩存和硬碟緩存 的緩存條目 從存在到删除可以看成一個完整的生命周期。為了友善debug,我們可以将(插入、更新、删除、清空等操作)看成一個個事件,然後建立一個監聽器監聽整個生命周期。
  • 8.享元模式:由于我們請求和緩存可能會比較頻繁,是以我們可以以對象池的形式複用對象,以減少大量建立和銷毀對象所需要的時間。

三、具體代碼實作##

務虛了這麼久,也應該務實了。先上項目例子 資料引擎架構項目源碼 ,建議大家下載下傳過來,然後結合部落格一起觀看下面我來分析一下資料引擎架構的實作代碼。

1.請求和傳回類設計

我們可以将所有的資料請求,當成類似于網絡請求的格式。這樣一來我們可以建立兩個類一個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,"對象池已空,建立一個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:我們在考慮請求的時候我總結了以下幾個在請求鍊之中需要用到的東西
    • 1.Object mParam:請求的參數,由于各種資料請求需要的參數是不同的,是以我們可以将請求參數設定為Object,在具體請求中進行更改
    • 2.CacheKey mCacheKey:CacheKey是一個接口,這個接口的實作類會作為記憶體和硬碟緩存時候的key來使用
    • 3.RequestFlag mRequestFlag:每一個不同的請求我們都需要用一個enum類來辨別。RequestFlag是一個接口,我們根據前面第一個請求的分類(NONE、GET、INSERT),實作了三個不同的enum,然後讓這三個enum實作RequestFlag。這樣一來就能用一個RequestFlag,辨別出一個請求的種類,一個RequestFlag中除了有請求的分類,裡面還有請求的層次(to_memory、to_local、to_network)。
    • 4.boolean isCacheToMemory:在請求從硬碟或者網絡傳回的時候,我們需要判斷該請求傳回的資料是否需要被記憶體緩存,預設是需要的
    • 5.boolean isSaveToLocal:在請求從網絡傳回的時候,我們需要判斷該請求傳回的資料是否需要被存儲在本地,預設是需要的
    • 6.boolean[] interceptorIsEnable:在請求鍊之中,我們可能需要屏蔽某些攔截器,此時可以在這裡設定哪些攔截器奏效,注意這裡的攔截是指請求上傳時候的攔截。
    • 7.除上面幾個參數,其他的參數都是為了建立對象池建構的,這裡的對象池使用了連結清單的方式,在使用obtain方法擷取的時候,将鍊首的對象取出。在使用recycle方法回收本對象的時候,将本對象設為鍊首。連結清單設定了最大值,當超過最大值的時候,就直接使用建立的對象
    • 8.在擷取Reuqest對象的時候,使用了由于要傳入的參數比較多,是以使用了靜态工廠模式和類似Builder的鍊式建立模式

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:這個類比較簡單,因為我們在用戶端使用的時候,可能會将一個資料結果轉化為多個實體供界面使用,是以有三個泛型參數可以設定。還有就是請求出錯的時候傳回的Exception,注意這裡的出錯僅僅指的是異常,其他類似于資料不符合的情況不在考慮之中。

2.攔截鍊的骨架

我們在前面的設計模式中提到了,在設計一個架構的時候需要面向接口程式設計,這樣才會符合前面說的設計模式六大原則。
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接口:我們前面提到的攔截器鍊,就是由一個個攔截器組成,而每個攔截器會實作Interceptor接口。####
    • 1.intercept方法就是每個攔截器要做的事情,在各個具體的攔截器中實作
    • 2.Chain其實就是整個攔截鍊的上下文,這個接口的實作會持有 攔截器鍊、Request和目前攔截器的編号。就相當于Chain負責調用了處理每個Interceptor的調用和傳回
  • 2.Service接口:我們不可能在Interceptor的intercept實作所有的邏輯,是以我們需要将攔截器中需要的操作封裝成一個個的服務,這樣能有效減小耦合。而每個Interceptor中持有的都隻是Service接口,不會持有具體的實作。
    • 1.前面我們說了,在某些情況下需要将攔截器設定為是否可用,前兩個方法就是這一個用處
    • 2.in方法表示要将參數in放入這個服務之中,典型的例子就是将傳回的資料放入記憶體緩存和硬碟緩存中
    • 3.out方法表示要從服務中傳回資料,典型的例子就是從記憶體、硬碟、記憶體緩存中傳回資料

3.攔截器鍊和攔截器的實作

我實作了四個攔截器:記憶體緩存攔截器-->新線程攔截器-->硬碟儲存攔截器-->網絡請求攔截器。和一個攔截器鍊,這個鍊中使用了上面四個攔截器。
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,"對象池已空,建立一個Chain  sRecycledCount:"+sRecycledCount);
    return new RealInterceptorChain(request);
}

private void reset() {
    mRequest = null;
    index=0;
}
}
           
  • 1.RealInterceptorChain:實作了前面說的Chain,有以下特點
    • 1.Request mRequest:該鍊的請求,用于所有的攔截器
    • 2.List<Interceptor> interceptors:攔截器鍊,存有所有攔截器
    • 3.int index:用于标記請求已經運作到了哪一個攔截器中
    • 4.其他的參數和Request類似,都是為構造對象池而設計
    • 5.proceed()方法:在請求開始的時候,這個方法會被調用。在擷取了攔截器1之後,将index++,會調用攔截器1的intercept方法,并傳入本Chain對象和該Reuqest中該攔截器是否起作用的标志isEnable。在攔截器1中會根據情況,适時調用本Chain對象的proceed()方法,這時又回到了這個方法,但是使用的攔截器變了。像這樣調用完了所有的攔截器之後會按層次傳回并在相應的攔截器中對傳回的參數進行處理。

@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()+"請求進入");

    if (requestFlag.getRequestLevel()==RequestLevel.to_memory){
        //在這種情況下,如果不開啟記憶體緩存,那麼就直接傳回null
        if (!isEnable) response=null;
        switch (requestFlag.getRequestType()){
            case GET:
                //傳回記憶體緩存中的結果
                response= memoryCacheService.out(request);
                break;
            case INSERT:
                //在更新記憶體緩存之後,傳回更新後的結果
                response= memoryCacheService.in(request,null);
                break;
            case NONE:
                //在memory下,不會有這種請求
                response= null;
                break;
        }
    }else {
        boolean isNeedToNextService=true;
        switch (requestFlag.getRequestType()){
            case GET:
                if (isEnable){
                    //記憶體緩存服務可以使用,就使用他
                    response= memoryCacheService.out(request);
                    //如果從記憶體緩存中擷取的結果不為null,那麼就不需要去下一個服務取資料
                    if (response!=null)isNeedToNextService=false;
                }
                //這裡不用 break 可以直接在需要進入下一個服務的情況下,複用代碼。
            case INSERT:
                // 如果請求是GET,表示從記憶體中擷取的結果是null,是以需要進入下一個服務擷取資料
                // 如果請求是INSERT 由于這種請求肯定要經過下一個服務,是以不需要判斷本服務是否可用
                // 下一個服務開始要去本地或者網絡擷取資料了,是以傳回的是 Observable
                if (isNeedToNextService)
                    response= ((Observable<?>)chain.proceed())
                            .map(new Func1<Object, Object>() {
                            @Override
                            public Object call(Object o) {
                                //如果回調後的結果不是null并且允許該資料被緩存(預設是允許的,除非在下一個服務傳回的時候禁止了),就緩存這個結果
                                if (o != null && request.isCacheToMemory())try {
                                    memoryCacheService.in(request, o);
                                } catch (Exception e) {
                                    //此時是 記憶體緩存的存儲出了問題
                                    FLog.e(TAG, "記憶體緩存的存儲出了問題", e);
                                    return Response.getFailedResponse(e);
                                }
                                return o;
                            }
                        });
                break;
            case NONE:
                //這種請求直接進入下一個服務,下一個服務開始要去本地或者網絡擷取資料了,是以傳回的是 Observable
                response= chain.proceed();
                break;
        }
    }
    FLog.v(TAG,request.getRequestFlag().toString()+"  "+TAG+"傳回");
    return response;
}

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

}

  • 2.MemoryCacheInterceptor是第一個攔截器,當調用RealInterceptorChain#proceed()的時候這個攔截器的intercept()會被調用。
    • 1.看代碼可知,整個MemoryCacheInterceptor都是使用Service來進行具體功能的實作,并沒有和具體的實作類耦合,這麼以來以後進行拓展或者更改Service的實作就會比較友善。
    • 2.整個方法中在開頭建立了幾個local對象,request和response不用多說,isEnable是一個标記該攔截器是否奏效的flag。隻有當Request中的flag和該Service的flag同時奏效,這個攔截器才會被奏效。其實也很好了解,Service的flag就相當于總開關,Request的flag就相當于臨時開關,隻有總開關和臨時開關都開啟的時候這個Service才會被使用。
    • 3.RequestFlag是一個很重要的對象,我們前面說了這個類中包含了RequestType和RequestLevel,這些都會在接下來的分派中使用到。
    • 4.接下來會根據RequestFlag中的RequestLevel進行一個選擇,如果這裡的RequestLevel是to_memory的話,那說明這個請求僅僅需要向記憶體中擷取資料,判斷完成了之後再根據RequestType進行插入或擷取資料。
    • 5.如果RequestFlag不是to_memory,那麼這個請求就有進入下一個攔截器的可能。
      • 1.GET:如果不是to_memory,那麼這個請求會先去記憶體中取資料,如果記憶體中沒有資料,就要進入其他線程去本地或者網絡取資料了
      • 2.INSERT:如果不是to_memory,又由于是插入,是以肯定需要将資料插入到本地或者伺服器中,是以使用了RxJava的線程切換,傳回的是一個Observable,在這個Observable的回調中,有些插入操作可能會傳回一些資料,此時就要根據是否有傳回資料和Request中的是否需要在記憶體中緩存資料來判斷是否需要進行記憶體緩存。Request#isCacheToMemory()這個參數可以在本地資料攔截器或者網絡攔截器中被修改,是以傳回的是否需要記憶體緩存,就看具體情況了。
      • 3.NONE:這種請求是必須去網絡中進行驗證的,是以既不需要記憶體緩存也不需要在從記憶體中擷取資料。由于需要切換線程,是以傳回的也是Observable。
    • 6.最後就是傳回Object response,這個response可能是一般的資料類,也可能是Observable,是以在調用處需要進行判斷,這在後面會講解到
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) {
                        //此時是本地存儲或者網絡請求出了問題
                        FLog.e(TAG,"本地存儲或者網絡請求出了問題",e);
                        return Response.getFailedResponse(e);
                    }
                }
            });
}

@Override
public Service getService() {
    return null;
}
}
           
  • 3.NewThreadInterceptor是第二個攔截器,用于适配Rxjava,會傳回一個Observable,線上程切換了之後會調用Chain#proceed()觸發下一個攔截器的intercept()。
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()+"請求進入");

    if (requestFlag.getRequestLevel()==RequestLevel.to_local){
        //在這種情況下,如果不開啟本地資料服務,那麼就直接傳回null
        if (!isEnable)return null;
        switch (requestFlag.getRequestType()){
            case GET:
                //傳回本地資料中的結果
                response= localDataService.out(request);
                break;
            case INSERT:
                //在更新記憶體緩存之後,傳回更新後的結果
                response= localDataService.in(request,null);
                break;
            case NONE:
                //根據傳入的資訊和記憶體緩存中的資訊,經過驗證操作後,驗證傳回結果
                response= localDataService.in(request,null);
                break;
        }
    }else {
        boolean isNeedToNextService=true;
        switch (requestFlag.getRequestType()){
            case GET:
                if (isEnable) {
                    //本地存儲服務可以使用,就使用他
                    response = localDataService.out(request);
                    //如果從本地儲存中擷取的結果不為null,那麼就不需要去下一個服務取資料
                    if (response != null) isNeedToNextService = false;
                    //這裡不用 break 可以直接在需要進入下一個服務的情況下,複用代碼。
                }
            case INSERT:
                // 如果請求是GET,表示從本地擷取的結果是null,是以需要進入下一個服務擷取資料
                // 如果請求是INSERT 由于這種請求肯定要經過下一個服務,是以不需要判斷本服務是否可用
                if (isNeedToNextService){
                    response= chain.proceed();
                    //如果下一個服務取來的結果不是null,并且這個資料被允許存在本地(預設是允許的,除非在下一個服務傳回的時候禁止了),,就把這個結果存起來
                    if (response!=null&&request.isSaveToLocal()) localDataService.in(request,response);
                }
                break;
            case NONE:
                //由于這種請求肯定要經過下一個服務,是以不需要判斷本服務是否可用,也不需要将傳回的結果存在本地
                response= chain.proceed();
                break;
        }
    }
    FLog.v(TAG,request.getRequestFlag()+"  "+TAG+"傳回");
    return response;
}

@Override
public Service getService() {
    return localDataService;
}
}
           
  • 3.第三個攔截器是LocalDataInterceptor,這個攔截器提供本地儲存服務,具體實作與請求分派方式和MemoryCacheInterceptor類似,不過也有幾個不同點
    • 1.在to_local的情況下NONE是有用處的。
    • 2.由于不需要像MemoryCacheInterceptor一樣切換線程,是以這裡的調用都是同步傳回,傳回的也都是資料類。
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()+"請求進入");
    //如果網絡服務不可用,就傳回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,這個就是網絡請求的攔截器了,由于網絡請求服務和本地儲存服務、記憶體服務不同,是以在網絡服務可用的情況下,隻會調用Service#out(),而Service#in()不會被調用。
整個攔截器鍊的實作就是上面這樣,可以看出雖然我并沒有講解各個攔截器中的服務具體是怎麼實作的,但是這并不影響整個攔截器鍊的邏輯。由于我們定義了Service這個抽象的接口,我們在攔截器鍊的實作過程中,并不需要去在意Service的具體邏輯,這就是将攔截器和服務解耦,而一旦解耦了,Service的實作類中無論如何變化,都影響不到整個攔截器鍊的架構。

由于字數太多:是以分成了兩篇:

Android資料層架構的實作 下篇