最近我们app的服务器吃不消了,所以我在为服务器增加缓存层之后,又想到在app端进行二级缓存以减少app对服务器的访问。我想很多app应该在项目的初期架构的时候就考虑到了这个问题,但是我当时开发这个app的时候完全不懂架构和设计模式,所以对性能根本没有考虑,导致了现在服务器经常崩溃。关于服务器的优化之后有时间再说,今天我们先来看看如何对一个app的数据的请求和缓存进行架构。
如何对请求进行归类?这是个问题,在Http请求里设定了八种请求的方式,但是显然并不适合我们这里的情况。我对我们app中的数据请求进行归类之后进行了一下两种分类
什么样的数据更适合在本地进行缓存呢?
前面我们说了两类可以在本地进行缓存的数据表,那么这两类数据该如何与服务器同步?
分析了上面的三个问题之后,我们得想想要使用什么样的方法来设计我们的数据引擎,熟悉java设计模式的同学可能会想到很多东西。我最近看了 Android源码设计模式解析与实战 之后也了解到了许多设计模式,现在的问题是怎么用这些设计模式呢?还好我最近又翻译了Fresco和OkHttp的源码,所以可以从这两个牛逼的开源项目中吸取一点经验。
务虚了这么久,也应该务实了。先上项目例子数据引擎架构项目源码,建议大家下载过来,然后结合博客一起观看下面我来分析一下数据引擎架构的实现代码。
我们可以将所有的数据请求,当成类似于网络请求的格式。这样一来我们可以创建两个类一个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) +
'}';
}
}
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;
}
}
我们在前面的设计模式中提到了,在设计一个架构的时候需要面向接口编程,这样才会符合前面说的设计模式六大原则。
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;
}
我实现了四个拦截器:内存缓存拦截器-->新线程拦截器-->硬盘储存拦截器-->网络请求拦截器。和一个拦截器链,这个链中使用了上面四个拦截器。
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;
}
}
@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;
}
}
@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) {
//此时是本地存储或者网络请求出了问题
FLog.e(TAG,"本地存储或者网络请求出了问题",e);
return Response.getFailedResponse(e);
}
}
});
}
@Override
public Service getService() {
return null;
}
}
@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()+"请求进入");
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;
}
}
@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()+"请求进入");
//如果网络服务不可用,就返回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;
}
}
整个拦截器链的实现就是上面这样,可以看出虽然我并没有讲解各个拦截器中的服务具体是怎么实现的,但是这并不影响整个拦截器链的逻辑。由于我们定义了Service这个抽象的接口,我们在拦截器链的实现过程中,并不需要去在意Service的具体逻辑,这就是将拦截器和服务解耦,而一旦解耦了,Service的实现类中无论如何变化,都影响不到整个拦截器链的框架。
由于字数太多:所以分成了两篇:Android数据层架构的实现 下篇