Android数据层架构的实现 下篇

简介: 接上篇:Android数据层架构的实现 上篇4.外观模式实现数据处理引擎框架暴露出来的api我们在使用各种开源框架的时候,大多数时候都不会对框架内部的实现进行细究,所以一个好的框架需要一个简单的入口来对整个框架进行使用,我接下来就是要讲解本框架的入口类。

接上篇:Android数据层架构的实现 上篇

4.外观模式实现数据处理引擎框架暴露出来的api

我们在使用各种开源框架的时候,大多数时候都不会对框架内部的实现进行细究,所以一个好的框架需要一个简单的入口来对整个框架进行使用,我接下来就是要讲解本框架的入口类。

public class DataEngine {
public static String TAG="DataEngine";
public static Context nowContext;
public static ObservableBoolean nowIsShowProgressBar;

public static final List<Interceptor> mInterceptors=new ArrayList<>();
public static final DataEngine sInstance;
static  {
    synchronized (DataEngine.class){
        sInstance=new DataEngine();
    }
}

private final Service memoryCacheService=new MemoryCacheServiceImpl(true);
private final Service mLocalDataService =new LocalDataServiceImpl(true) ;
private final Service networkService=new NetworkServiceImpl(true);
private DataEngine() {
    mInterceptors.add(new MemoryCacheInterceptor(memoryCacheService));
    mInterceptors.add(new NewThreadInterceptor());
    mInterceptors.add(new LocalDataInterceptor(mLocalDataService));
    mInterceptors.add(new NetworkInterceptor(networkService));
}

/**
 * 所有的数据请求,都是从这个方法开始。本方法在{@link LocalDataEngine}和{@link NetworkDataEngine}中使用。
 * @param request 传入的数据请求
 * @return
 */
public Observable<Response> request(final Request request){
    final RealInterceptorChain realInterceptorChain=RealInterceptorChain.obtain(request);
    Object response;
    try {
        response = realInterceptorChain.proceed();
    } catch (Exception e) {
        e.printStackTrace();
        // 此时抛出的异常,那么就是请求失败了,应该是内存缓存服务有问题,由于发生了未知的错误所以不应该继续运行这个请求了。
        FLog.e(TAG,"应该是内存缓存服务有问题",e);
        return Observable.just(Response.getFailedResponse(e))
                .compose(((RxAppCompatActivity)nowContext).<Response>bindToLifecycle())
                .filter(new Func1<Response, Boolean>() {
                    @Override
                    public Boolean call(Response appConfigObjectObjectResponse) {
                        //此时拦截链已经调用完毕,所以可以将RealInterceptorChain回收
                        realInterceptorChain.recycle();
                        Toast.makeText(nowContext, "操作失败,发生未知错误", Toast.LENGTH_SHORT).show();
                        if (nowIsShowProgressBar!=null)nowIsShowProgressBar.set(false);
                        return appConfigObjectObjectResponse.isSuccess();
                    }
                });
    }

    Observable<?> finalResponseObservable;
    if (response instanceof Observable){
        // 这里表示需要从其他线程获取数据数据,一般是本地储存或者网络上的数据
        finalResponseObservable = (Observable<?>) response;
    } else {
        // 这里表示直接从本线程获取数据,一般是内存中的数据
        finalResponseObservable = Observable.just(response);
    }
    return finalResponseObservable
            .compose(((RxAppCompatActivity)nowContext).bindToLifecycle())
            .observeOn(AndroidSchedulers.mainThread())
            .filter(new Func1<Object, Boolean>() {
                @Override
                public Boolean call(Object o) {
                    //此时拦截链已经调用完毕,所以可以将RealInterceptorChain回收
                    realInterceptorChain.recycle();
                    if (o instanceof Response){
                        // 这里表示在 本地储存或者网络请求或者想内存缓存存数据的时候 发生了问题,所以直接返回了请求错误的结果,
                        // 由于发生了未知的错误所以不应该继续运行这个请求了。
                        Toast.makeText(nowContext, "操作失败,发生未知错误", Toast.LENGTH_SHORT).show();
                        if (nowIsShowProgressBar!=null)nowIsShowProgressBar.set(false);
                        return ((Response) o).isSuccess();
                    }
                    return true;
                }
            }).map(new Func1<Object, Response>() {
                @Override
                public Response call(Object o) {
                    FLog.v(TAG, request.getRequestFlag()+"请求成功");
                    FLog.v("                                         ","                                     ");
                    return Response.getCommonResponseOne(o);
                }
            });
}
}

由于本框架适配了Rxjava,所以需要大家对Rxjava比较熟悉

  • 1.前三个静态变量不需要细究,List<Interceptor> mInterceptors是静态final变量,在DataEngine单例创建的时候会把拦截器放入其中。DataEngine sInstance是静态final单例,使用同步的方式在类被加载的时候初始化,由于DataEngine的构造器是private的,所以这个类不能再创建其他对象了。
  • 2.我们由代码可以看出,所有的Service和List<Interceptor> mInterceptors都是被多线程共用的,所以此时就会出现线程安全问题。由于mInterceptors中的拦截器都是不可变类,所以这个不需要担心。但是所有Service的实现就需要对某些操作进行同步了,Service的实现和同步会在后面讲到。
  • 3.再下来我们就可以看见,惟一一个public的方法request(final Request request):
    • 1.方法首先会去RealInterceptorChain的对象池中获取一个对象,然后调用realInterceptorChain#proceed()获取结果。注意这里的调用都是在主线程进行的,如果大家对前面的MemoryCacheInterceptor还有印象的话,就会知道这里要么返回数据类Object,要么返回一个Observable。数据类Object是从内存中获取的不会在其他线程,而Observable并没有调用subscribe(),所以其只是对一个请求的封装,真正的请求还没被调用。
    • 2.在获取到response之后,要判断这个response到底是数据类Object还是Observable,如果是数据类Object将其再用Observable封装。
    • 3.再进行了上面的操作之后,返回的请求就都变成Observable了,此时我们先将线程切换为主线程,然后用一个filter过滤掉出现异常的请求,最后将成功获取的数据映射成一个Response。注意:此时返回的是Observable,真正的调用会由客户端触发

5.服务的实现

前面我们在讲拦截链的时候,使用的都是Service接口,那么每个拦截器的具体实现是怎么样的呢?接下来我就来讲解一下我们app登录时,每个服务的实现。

@ThreadSafe

public class MemoryCacheServiceImpl implements Service{
private final MemoryCache<CacheKey,Object> mainMemoryCache=new CountingLruMapMemoryCacheImpl<CacheKey, Object>(200,new DefaultCacheListener());
private final MemoryCache minorMemoryCache=null;
private boolean enable;

public MemoryCacheServiceImpl(boolean enable) {
    this.enable = enable;
}

@Override
public Object in(Request request, Object in) throws Exception {
    return mainMemoryCache.cache(request.getCacheKey(),in==null?request.getParam():in);
}

@Override
public Object out(Request request) throws Exception {
    return mainMemoryCache.get(request.getCacheKey());
}

@Override
public boolean isEnabled() {
    return enable;
}

@Override
public void setEnable(boolean enable) {
    this.enable=enable;
}

}
  • 1.MemoryCacheServiceImpl是内存服务的实现类。

      1. MemoryCache<CacheKey,Object> mainMemoryCache:是一个接口,由于我们可以使用第三方类库实现缓存,也可以写一个自制缓存,如果在内存缓存服务中使用实际的类,那么在以后要进行更换内存缓存实现的时候就需要改动很多地方。而MemoryCache<CacheKey,Object>就是我定义了一组内存缓存组件的标准,只要实现了这个标准的内存缓存组件都能用于这个内存缓存服务。
    • 2.MemoryCache minorMemoryCache:是副缓存,目前没有赋值。
    • 3.接下来的四个方法就是对Service的实现,可以看见对于in()代码中就直接调用的是缓存组件的cache()来对,传入的参数或者返回的结果进行缓存。out()中就是直接从缓存组件中根据Request中的CacheKey获取缓存的数据。

    代码

          public interface ToLocalDataRequest<OUT,IN> {
                  OUT updateAndGetAppConfig(Request request, IN in)throws Exception;
                  OUT getAppConfig(Request request, IN in)throws Exception ;
                  OUT updateAndGetUserConfig(Request request, IN in) throws Exception;
                  OUT getUserConfig(Request request, IN in)throws Exception ;
          }
          public interface ToNetworkRequest<OUT,IN> {
             OUT patrolAccountAction_login(Request request, IN in)throws Exception ;
              OUT initAppData(Request request, IN in)throws Exception ;
          }
    
  • 2.在讲本地储存服务和网络服务的时候,先来介绍两个接口。ToLocalDataRequest<OUT,IN>和ToNetworkRequest<OUT,IN>。我们都知道内存缓存中存取数据是比较简单的,但是到了硬盘和服务器上就不一样了。有时我们会用数据库实现硬盘缓存,有时候会用文件系统实现硬盘缓存,在这个时候对于不同种类的请求要进行的操作是不同的,同样对于向服务器的请求也是一样的。所以我们需要对本地存储请求和服务器请求分别创建接口,然后让本地存储服务和网络请求服务去实现他们,最后根据不同的请求来调用不同的被覆盖的方法。

@ThreadSafe

public class LocalDataServiceImpl implements Service,ToLocalDataRequest,ToNetworkRequest{
private static String TAG="LocalDataServiceImpl";

private final DaoMaster daoMaster = new DaoMaster(new DaoMaster.DevOpenHelper(MyApplication.myApplication, "my.db", null).getWritableDb());
private final DiskCache<SimpleCacheKey> sharePreferenceDiskCache =new SharePreferenceDiskCacheImpl<>(MyApplication.myApplication,new DefaultCacheListener());
private boolean enable;

public LocalDataServiceImpl(boolean enable) {
    this.enable = enable;
}

@Override
public Object in(Request request,Object in) throws Exception {
    return TransformDataMethodDispatch.dispatch(this,this,request,in);
}

@Override
public Object out(Request request) throws Exception {
    return TransformDataMethodDispatch.dispatch(this,this,request,null);
}

@Override
public Object patrolAccountAction_login(Request request, Object in) throws IOException {
    return null;
}

@Override
public Object initAppData(Request request, Object in) throws IOException {
    if (in==null)return null;
    FlushData flushData=(FlushData)in;
    ArrayList<MissionInfoEntity> mInfoEntityArrayList=flushData.getFlushData().getInfoEntityArrayList();
    ArrayList<PolicePeopleEntity> mPeopleArrayList=flushData.getFlushData().getPeopleArrayList();
    ArrayList<UnitBaseEntity> mUnitBaseArrayList=flushData.getFlushData().getUnitBaseArrayList();

    if (mInfoEntityArrayList!=null&&mInfoEntityArrayList.size()!=0){
        for (MissionInfoEntity m:mInfoEntityArrayList){
            daoMaster.newSession().getMissionInfoDao().insert(new MissionInfo(m));
        }
    }

    if (mPeopleArrayList!=null&&mPeopleArrayList.size()!=0){
        for (PolicePeopleEntity p :mPeopleArrayList){
            daoMaster.newSession().getPolicePeopleDao().insert(new PolicePeople(p));
        }
    }

    if (mUnitBaseArrayList!=null&&mUnitBaseArrayList.size()!=0){
        for (UnitBaseEntity u :mUnitBaseArrayList){
            daoMaster.newSession().getUnitBaseDao().insert(new UnitBase(u));
        }
    }
    request.setCacheToMemory(false);
    FLog.v(TAG,"向数据库初始化数据成功");
    return in;
}

@Override
public Object updateAndGetAppConfig(Request request, Object in) throws Exception {
    if (request.getParam()==null){
        FLog.e(TAG,"appConfig插入时为null");
        throw new NullPointerException();
    }
    AppConfig appConfig=(AppConfig)request.getParam();
    List<DBMap> dbMapList=appConfig.transformToDBMap();
    daoMaster.newSession().getDBMapDao().insertOrReplaceInTx(dbMapList);
    FLog.v(TAG,"向数据库更新appConfig成功");
    return appConfig;
}

@Override
public Object getAppConfig(Request request, Object in){
    AppConfig appConfig = null;
    List<DBMap> dbMapList=daoMaster.newSession().getDBMapDao().loadAll();
    if (dbMapList==null||dbMapList.size()==0)return null;
    appConfig=new AppConfig(dbMapList);
    FLog.v(TAG,"从数据库获取的appConfig");
    return appConfig;
}

@Override
public Object updateAndGetUserConfig(Request request, Object in) throws Exception {
    if (request.getParam()==null){
        FLog.e(TAG,"userConfig插入时为null");
        throw new NullPointerException();
    }
    UserConfig userConfig=(UserConfig) request.getParam();
    sharePreferenceDiskCache.cache((SimpleCacheKey) request.getCacheKey(),JsonUtil.objectToJson(userConfig));
    FLog.v(TAG,"向sharePreferenceDiskCache更新userConfig成功");
    return userConfig;
}

@Override
public Object getUserConfig(Request request, Object in) throws IOException {
    UserConfig userConfig= JsonUtil.jsonToObject((String) sharePreferenceDiskCache.get((SimpleCacheKey) request.getCacheKey()),UserConfig.class);
    FLog.v(TAG,"从sharePreferenceDiskCache获取的userConfig");
    return userConfig;
}

@Override
public boolean isEnabled() {
    return enable;
}

@Override
public void setEnable(boolean enable) {
    this.enable=enable;
}
}
  • 3.LocalDataServiceImpl是本地存储服务的实现类,可以看见这个类实现了Service,ToLocalDataRequest,ToNetworkRequest这三个接口,Service不必多说。可能有人要问为什么要实现ToNetworkRequest呢?那是因为在一些向服务器的请求之中,可能在本地中已经有缓存了,那么此时并不需要去服务器中取数据。我们在前面的本地储存拦截器中也有介绍到这一点。
    • 1.可以看见这个类中有GreenDao(一种数据库的ORM)和DiskCache<SimpleCacheKey> sharePreferenceDiskCache这两个实现,GreenDao内部实现不是由我们实现的所以不需要抽象为接口。而sharePreferenceDiskCache是一个本地储存抽象出来的接口和前面讲的内存缓存接口是一个道理。我目前用的是sharePreference,当然以后有需要还可以换成用文件系统实现,换起来也很方便。
    • 2.接下来是实现了Service的in()和out()方法,本来是需要在这两个方法中根据请求的种类使用switch将请求分派到各个方法中的,这里我使用了TransformDataMethodDispatch来分派,内部实现是一样的。
    • 3.接下来的几个方法就是对ToLocalDataRequest,ToNetworkRequest这两个接口的实现了,这里需要根据具体的业务逻辑进行考虑。

@ThreadSafe

public class NetworkServiceImpl implements Service,ToNetworkRequest{
private boolean enable;

public NetworkServiceImpl(boolean enable) {
    this.enable = enable;
}

@Override
public Object in(Request request, Object in) throws Exception {
    return null;
}

@Override
public Object out(Request request) throws Exception {
    return TransformDataMethodDispatch.dispatch(this,null,request,null);
}

@Override
public Object patrolAccountAction_login(Request request, Object in) throws IOException {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    LoginEntity.Response response=new LoginEntity.Response(new LoginEntity.UserEntity(1,"13030512957","1",(byte) 1,"男"),"杭州",1,4,"");
    return response;
}

@Override
public Object initAppData(Request request, Object in) throws IOException {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return null;
}

@Override
public boolean isEnabled() {
    return enable;
}

@Override
public void setEnable(boolean enable) {
    this.enable = enable;
}
}
  • 4.NetworkServiceImpl就是网络请求服务的实现了
    • 1.这里我只是模拟了网络请求,大家可以使用任何网络框架比如:Retrofit Okhttp之类的
    • 2.注意这里的in()方法是空实现,是不会被调用到的,前面在介绍拦截器的时候已经说过了,不再赘述。
    • 3.和本地存储服务一样,out()方法中也使用了TransformDataMethodDispatch进行请求的分派。

以上就是Service的各个实现类,大家可能发现了,我并没有讲解类似于内存缓存,本地储存以及网络请求的具体实现,但是整个逻辑依旧是非常清晰的。所以看到这里我想有些同学已经理解了,到底该如何设计一个框架:框架的每个层次都需要使用接口进行抽象,能使用接口进行交互的地方就别使用实现类,在改变了一个具体子功能的实现之后其他模块并不会受到影响。 能做到前面说的几点,我想写出来的代码应该算是一个比较容易扩展的架构了。

6.更加上层的封装

我们前面将了DataEngine就算是我们的暴露给客户端的api了,但是这个类用起来还是有点麻烦的,比如说我们的Reponse的泛型参数还没有用起来,这样可能在某些地方需要我们手动进行类型转化,一不小心可能还会将类型转错了。所以接下来我们再在DataEngine之上封装一层api。

public class Transform<Response1,Response2 ,Response3> implements ToLocalDataRequest<Observable<Response<Response1,Response2,Response3>>,Observable<Response>>,
    ToNetworkRequest<Observable<Response<Response1,Response2,Response3>>,Observable<Response>> {

public static <Response1> Observable<Response<Response1,Object,Object>> transformOne(Request request) {
    Transform<Response1,Object,Object> transform=new Transform<>();
    try {
        return TransformDataMethodDispatch.dispatch(transform,transform,request, DataEngine.sInstance.request(request));
    } catch (Exception e) {
        e.printStackTrace();
        return Observable.just(Response.<Response1,Object,Object>getFailedResponse(e));
    }
}

public static <Response1,Response2> Observable<Response<Response1,Response2,Object>> transformTwo(Request request) {
    Transform<Response1,Response2,Object> transform=new Transform<>();
    try {
        return TransformDataMethodDispatch.dispatch(transform,transform,request, DataEngine.sInstance.request(request));
    } catch (Exception e) {
        e.printStackTrace();
        return Observable.just(Response.<Response1,Response2,Object>getFailedResponse(e));
    }
}
public static <Response1,Response2,Response3> Observable<Response<Response1,Response2,Response3>> transformThree(Request request) {
    Transform<Response1,Response2,Response3> transform=new Transform<>();
    try {
        return TransformDataMethodDispatch.dispatch(transform,transform,request, DataEngine.sInstance.request(request));
    } catch (Exception e) {
        e.printStackTrace();
        return Observable.just(Response.<Response1,Response2,Response3>getFailedResponse(e));
    }
}


@Override
public Observable<Response<Response1, Response2, Response3>> updateAndGetAppConfig(Request request, Observable<Response> responseObservable) throws Exception {
    return responseObservable
            .map(new Func1<Response, Response<Response1, Response2, Response3>>() {
                @Override
                public Response<Response1, Response2, Response3> call(Response response) {
                    return response;
                }
            });
}

@Override
public Observable<Response<Response1, Response2, Response3>> getAppConfig(Request request, Observable<Response> responseObservable) throws Exception {
    return responseObservable
            .map(new Func1<Response, Response<Response1, Response2, Response3>>() {
                @Override
                public Response<Response1, Response2, Response3> call(Response response) {
                    return response;
                }
            });
}

@Override
public Observable<Response<Response1, Response2, Response3>> updateAndGetUserConfig(Request request, Observable<Response> responseObservable) throws Exception {
    return responseObservable
            .map(new Func1<Response, Response<Response1, Response2, Response3>>() {
                @Override
                public Response<Response1, Response2, Response3> call(Response response) {
                    return response;
                }
            });
}

@Override
public Observable<Response<Response1, Response2, Response3>> getUserConfig(Request request, Observable<Response> responseObservable) throws Exception {
    return responseObservable
            .map(new Func1<Response, Response<Response1, Response2, Response3>>() {
                @Override
                public Response<Response1, Response2, Response3> call(Response response) {
                    return response;
                }
            });
}

@Override
public Observable<Response<Response1, Response2, Response3>> patrolAccountAction_login(Request request, Observable<Response> responseObservable) throws Exception {
    return responseObservable
            .map(new Func1<Response, Response<Response1, Response2, Response3>>() {
                @Override
                public Response<Response1, Response2, Response3> call(Response response) {
                    return response;
                }
            });
}

@Override
public Observable<Response<Response1, Response2, Response3>> initAppData(Request request, Observable<Response> responseObservable) throws Exception {
    return responseObservable
            .map(new Func1<Response, Response<Response1, Response2, Response3>>() {
                @Override
                public Response<Response1, Response2, Response3> call(Response response) {
                    return response;
                }
            });
}
}
  • 1.这个类就是对DataEngine的封装,这个封装可以将DataEngine#request()中返回的数据类,转换成多个界面上可以直接使用的数据类。当由于不同种类的请求用到的数据类型和数量各不相同,所以这个类需要实现ToLocalDataRequest和ToNetworkRequest这两个接口,对不同的请求返回的结果,进行不同的转换。
    • 1.transformOne()、transformTwo()、transformThree()这三个方法,分别表示最后转换的结果是一个、两个、三个。
    • 2.后面的方法都是两个接口的实现,我举的例子中都是不需要转换的数据请求。

7.例子代码

我前面已经给出了一个app登录界面的整套运行流程,项目已经测试过了问题应该不大有问题可以加我QQ,内部有数据引擎的完整代码,项目是用MVVM+Rxjava+GreenDao写的,大家有兴趣可以去下载看看,能给个star就更好了,没兴趣的同学给大家欣赏一下我们登录界面的链式代码。不得不说Rxjava让代码逻辑变得非常清晰,而MVVM的databinding则完美的与数据引擎结合了起来。

`   isShowProgressBar.set(true);
    LocalDataEngine.getAppConfig()
            .flatMap(new Func1<AppConfig, Observable<AppConfig>>() {
                @Override
                public Observable<AppConfig> call(AppConfig appConfig) {
                    //如果为null表示第一次打开app,所以进行全局数据初始化
                    if (appConfig==null) appConfig=new AppConfig(true,"13030512957",false,false);
                    FLog.v(TAG,String.valueOf(appConfig.isFirstOpenApp()));
                    //如果不是第一次打开app,将appConfig传入下面一个环节
                    appConfig.setAutoLogin(true);
                    appConfig.setRememberPassword(true);
                    if (appConfig.isFirstOpenApp()){
                        return LocalDataEngine.updateAndGetAppConfig(appConfig);
                    }else {
                        return Observable.just(appConfig);
                    }
                }
            }).flatMap(new Func1<AppConfig, Observable<AppConfig>>() {
                @Override
                public Observable<AppConfig> call(AppConfig appConfig) {
                    //如果是第一次打开app,那么去服务器拉去数据表
                    if (appConfig.isFirstOpenApp()){
                        return NetworkDataEngine.initAppData(appConfig);
                    }else {
                        //否则,将appConfig传入下面一个环节
                        return Observable.just(appConfig);
                    }
                }
            }).filter(new Func1<AppConfig, Boolean>() {
                @Override
                public Boolean call(AppConfig appConfig) {
                    //此时如果是第一次打开app,那么数据已经全部初始化完毕,更变第一次打开app的flag
                    //同时AppConfig已经准备好,可以进行接下来的操作了。
                    if (appConfig.isFirstOpenApp())
                        appConfig.setFirstOpenApp(false);
                    return true;
                }
            }).filter(new Func1<AppConfig, Boolean>() {
                @Override
                public Boolean call(AppConfig appConfig) {
                    //设置界面上的:记住密码 和 自动登录 的checkBox
                    isRememberPassword.set(appConfig.isRememberPassword());
                    isAutoLogin.set(appConfig.isAutoLogin());
                    //如果全局设置中,既不需要记住密码,也不需要自动登录,那么就不去获取默认的用户信息,直接显示登录界面。
                    if ((!appConfig.isRememberPassword())&&(!appConfig.isAutoLogin())){
                        isShowProgressBar.set(false);
                        return false;
                    }
                    return true;
                }
            }).flatMap(new Func1<AppConfig, Observable<UserConfig>>() {
                @Override
                public Observable<UserConfig> call(AppConfig appConfig) {
                    //根据全局配置获取默认用户的帐号,然后去SharePreference中获取默认用户数据
                    return LocalDataEngine.getUserConfig(appConfig.getNowUserPhone());
                }
            }).filter(new Func1<UserConfig, Boolean>() {
                @Override
                public Boolean call(UserConfig userConfig) {
                    // 如果默认用户数据为null,说明用户数据缓存被清空了,或者app是第一次被打开,此时app不需要自动登录。
                    if (userConfig==null){
                        //弹出toast说明自动登录失败的原因,并将登录界面显示出来
                        Toast.makeText(mBaseActivity, "用户缓存被清空,请手动登录", Toast.LENGTH_SHORT).show();
                        isShowProgressBar.set(false);
                        return false;
                    }else {
                        //数据准备就绪,进行自动登录
                        usernameObservable.set(userConfig.getRememberedPhone());
                        passwordObservable.set(userConfig.getRememberedPassword());
                        username=usernameObservable.get();
                        password=passwordObservable.get();
                        return true;
                    }
                }
            }).subscribe(new Action1<UserConfig>() {
                @Override
                public void call(UserConfig userConfig) {
                    login();
                }
            });`

本篇博客只是给大家一个启示,告诉大家如何实现一个数据引擎的架构,内部必然会出现一些纰漏,有兴趣的同学可以和我在QQ上交流。

最后大家对MVVM架构有兴趣的可以看看我的这两篇博客

MVVM架构篇之databinding源码解析

MVVM架构之自动增删改的极简RecycleView的实现

相关文章
|
29天前
|
存储 SQL 关系型数据库
ClickHouse(02)ClickHouse架构设计介绍概述与ClickHouse数据分片设计
ClickHouse的核心架构包括执行过程和数据存储两部分。执行过程涉及Parser与Interpreter解析SQL,通过Column、DataType、Block、Functions和Storage模块处理数据。Column是内存中列的表示,Field处理单个值,DataType负责序列化和反序列化,Block是内存中表的子集,Block Streams处理数据流。Storage代表表,使用不同的引擎如StorageMergeTree。数据存储基于分片和副本,1个分片由多个副本组成,每个节点只能拥有1个分片。
70 0
ClickHouse(02)ClickHouse架构设计介绍概述与ClickHouse数据分片设计
|
1月前
|
数据库 Android开发 开发者
构建高性能微服务架构:从理论到实践构建高效Android应用:探究Kotlin协程的优势
【2月更文挑战第16天】 在当今快速迭代和竞争激烈的软件市场中,微服务架构以其灵活性、可扩展性和独立部署能力而受到企业的青睐。本文将深入探讨如何构建一个高性能的微服务系统,涵盖从理论基础到具体实现的各个方面。我们将重点讨论服务拆分策略、通信机制、数据一致性以及性能优化等关键主题,为读者提供一个清晰、实用的指南,以便在复杂多变的业务环境中构建和维护健壮的微服务体系结构。 【2月更文挑战第16天】 在移动开发领域,性能优化和流畅的用户体验是至关重要的。随着技术的不断进步,Kotlin作为一种现代编程语言,在Android开发中被广泛采用,尤其是其协程特性为异步编程带来了革命性的改进。本文旨在深入
239 5
|
3月前
|
安全 API Android开发
Android网络和数据交互: 解释Retrofit库的作用。
Android网络和数据交互: 解释Retrofit库的作用。
38 0
|
2月前
|
缓存 安全 API
【亿级数据专题】「高并发架构」盘点本年度探索对外服务的百万请求量的API网关设计实现
公司对外开放的OpenAPI-Server服务,作为核心内部系统与外部系统之间的重要通讯枢纽,每天处理数百万次的API调用、亿级别的消息推送以及TB/PB级别的数据同步。经过多年流量的持续增长,该服务体系依然稳固可靠,展现出强大的负载能力。
55 9
【亿级数据专题】「高并发架构」盘点本年度探索对外服务的百万请求量的API网关设计实现
|
3天前
|
Android开发 开发者
Android网络和数据交互: 请解释Android中的AsyncTask的作用。
Android&#39;s AsyncTask simplifies asynchronous tasks for brief background work, bridging UI and worker threads. It involves execute() for starting tasks, doInBackground() for background execution, publishProgress() for progress updates, and onPostExecute() for returning results to the main thread.
3 0
|
3天前
|
网络协议 安全 API
Android网络和数据交互: 什么是HTTP和HTTPS?在Android中如何进行网络请求?
HTTP和HTTPS是网络数据传输协议,HTTP基于TCP/IP,简单快速,HTTPS则是加密的HTTP,确保数据安全。在Android中,过去常用HttpURLConnection和HttpClient,但HttpClient自Android 6.0起被移除。现在推荐使用支持TLS、流式上传下载、超时配置等特性的HttpsURLConnection进行网络请求。
5 0
|
17天前
|
XML Java Android开发
Android每点击一次按钮就添加一条数据
Android每点击一次按钮就添加一条数据
21 1
|
18天前
|
移动开发 前端开发 数据管理
构建高效Android应用:采用MVVM架构与LiveData的全面指南
在移动开发领域,构建一个既快速又可靠的应用对于开发者来说至关重要。随着Android Jetpack组件的推出,MVVM(Model-View-ViewModel)架构和LiveData已成为实现响应式、可测试且易于维护应用的首选解决方案。本文将深入探讨如何在Android应用中实施MVVM模式,以及如何利用LiveData来优化UI组件的数据更新流程,确保用户界面与业务逻辑之间的高度解耦和流畅交互。
18 4
|
1月前
|
SQL 缓存 分布式计算
日增数据超10PB!揭秘沃尔玛Lakehouse架构选型之路
日增数据超10PB!揭秘沃尔玛Lakehouse架构选型之路
44 2
|
1月前
|
存储 SQL 机器学习/深度学习
通用数据湖仓一体架构正当时
通用数据湖仓一体架构正当时
59 2