Android数据层架构的实现 上篇

简介: 最近我们app的服务器吃不消了,所以我在为服务器增加缓存层之后,又想到在app端进行二级缓存以减少app对服务器的访问。我想很多app应该在项目的初期架构的时候就考虑到了这个问题,但是我当时开发这个app的时候完全不懂架构和设计模式,所以对性能根本没有考虑,导致了现在服务器经常崩溃。

最近我们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,所以在调用处需要进行判断,这在后面会讲解到

@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;
}
}
  • 3.NewThreadInterceptor是第二个拦截器,用于适配Rxjava,会返回一个Observable,在线程切换了之后会调用Chain#proceed()触发下一个拦截器的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()+"请求进入");

    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一样切换线程,所以这里的调用都是同步返回,返回的也都是数据类。

@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;
}
}
  • 4.第四个拦截器是NetworkInterceptor,这个就是网络请求的拦截器了,由于网络请求服务和本地储存服务、内存服务不同,所以在网络服务可用的情况下,只会调用Service#out(),而Service#in()不会被调用。

整个拦截器链的实现就是上面这样,可以看出虽然我并没有讲解各个拦截器中的服务具体是怎么实现的,但是这并不影响整个拦截器链的逻辑。由于我们定义了Service这个抽象的接口,我们在拦截器链的实现过程中,并不需要去在意Service的具体逻辑,这就是将拦截器和服务解耦,而一旦解耦了,Service的实现类中无论如何变化,都影响不到整个拦截器链的框架。

由于字数太多:所以分成了两篇:Android数据层架构的实现 下篇

相关文章
|
26天前
|
存储 SQL 关系型数据库
ClickHouse(02)ClickHouse架构设计介绍概述与ClickHouse数据分片设计
ClickHouse的核心架构包括执行过程和数据存储两部分。执行过程涉及Parser与Interpreter解析SQL,通过Column、DataType、Block、Functions和Storage模块处理数据。Column是内存中列的表示,Field处理单个值,DataType负责序列化和反序列化,Block是内存中表的子集,Block Streams处理数据流。Storage代表表,使用不同的引擎如StorageMergeTree。数据存储基于分片和副本,1个分片由多个副本组成,每个节点只能拥有1个分片。
67 0
ClickHouse(02)ClickHouse架构设计介绍概述与ClickHouse数据分片设计
|
1月前
|
数据库 Android开发 开发者
构建高性能微服务架构:从理论到实践构建高效Android应用:探究Kotlin协程的优势
【2月更文挑战第16天】 在当今快速迭代和竞争激烈的软件市场中,微服务架构以其灵活性、可扩展性和独立部署能力而受到企业的青睐。本文将深入探讨如何构建一个高性能的微服务系统,涵盖从理论基础到具体实现的各个方面。我们将重点讨论服务拆分策略、通信机制、数据一致性以及性能优化等关键主题,为读者提供一个清晰、实用的指南,以便在复杂多变的业务环境中构建和维护健壮的微服务体系结构。 【2月更文挑战第16天】 在移动开发领域,性能优化和流畅的用户体验是至关重要的。随着技术的不断进步,Kotlin作为一种现代编程语言,在Android开发中被广泛采用,尤其是其协程特性为异步编程带来了革命性的改进。本文旨在深入
238 5
|
3月前
|
安全 API Android开发
Android网络和数据交互: 解释Retrofit库的作用。
Android网络和数据交互: 解释Retrofit库的作用。
38 0
|
3月前
|
安全 网络协议 API
Android网络和数据交互: 什么是HTTP和HTTPS?在Android中如何进行网络请求?
Android网络和数据交互: 什么是HTTP和HTTPS?在Android中如何进行网络请求?
27 0
|
3月前
|
Android开发 开发者
Android网络和数据交互: 请解释Android中的AsyncTask的作用。
Android网络和数据交互: 请解释Android中的AsyncTask的作用。
21 0
|
4月前
|
XML 物联网 API
Android Ble蓝牙App(五)数据操作
Android Ble蓝牙App(五)数据操作
|
2月前
|
缓存 安全 API
【亿级数据专题】「高并发架构」盘点本年度探索对外服务的百万请求量的API网关设计实现
公司对外开放的OpenAPI-Server服务,作为核心内部系统与外部系统之间的重要通讯枢纽,每天处理数百万次的API调用、亿级别的消息推送以及TB/PB级别的数据同步。经过多年流量的持续增长,该服务体系依然稳固可靠,展现出强大的负载能力。
55 9
【亿级数据专题】「高并发架构」盘点本年度探索对外服务的百万请求量的API网关设计实现
|
14天前
|
XML Java Android开发
Android每点击一次按钮就添加一条数据
Android每点击一次按钮就添加一条数据
21 1
|
1月前
|
SQL 缓存 分布式计算
日增数据超10PB!揭秘沃尔玛Lakehouse架构选型之路
日增数据超10PB!揭秘沃尔玛Lakehouse架构选型之路
40 2
|
1月前
|
存储 SQL 机器学习/深度学习
通用数据湖仓一体架构正当时
通用数据湖仓一体架构正当时
56 2