本文首發(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ī)編程。下面是我的微信公眾號:世界上有意思的事,干貨多多等你來看。
