题外话:去年十二月份进行封闭式开发,我就打算对代码进行重构,重构的原因是代码层次不清晰,
Activity职责太多,Activity不仅要获取接口数据,还要对接口数据进行缓存,代码中充斥中各种数据状态判断以及层层回调,
更重要的是这样写不便于单独修改某一个接口实现,也不便于测试,更不便于mock数据,mock数据不依赖于接口返回,能自测很多状态切换逻辑,是多么重要。
我想要的效果:
- 数据调用者(Activity或Fragment)不需要知道数据来自网络还是缓存,直接拿到解析好的Entity使用即可,但调用者能强制获取网络数据
- 不同的接口可以使用不同的缓存方案,比如文件或者数据库
- 不动调用者代码,能mock数据,每层之间便于测试
- 层与层之间面向接口,低耦合
站在巨人的肩膀上
在思考架构设计的时候,主要参考了clean架构的思想,clean是基于MVP,多抽象出domain层,确实很优雅,却有些繁琐,但它的Repository思想和Dagger2依赖注入,让我的思想提升一大截。
图
这张图是我用win画图随手画的,能看就行;整体还是MVC思想,其中:
- Activity和Fragment还是保留原本V和C的职责,这一点没有想MVP靠拢
- Model层主要由Repository模块,Api模块,Cache模块,Entity实体类和一些network、文件、数据库操作的工具类组成
- Repository作为Model层的门面 ,屏蔽底层实现,向调用层提供数据交付,Repository依赖Api层和Cache层提供的接口
- Api层主要是服务器接口数据的封装,向下调用一堆网络请求封装的工具类
- Cache层对接口数据进行本地缓存向下调用一堆文件操作或者数据库操作的工具类
- Entity层作为实体,ApiResponse 做接口数据包裹,做统一泛型解析。
Model层剖析
ApiResponse是什么鬼
这要从服务器接口返回格式来说起,比如接口返回的是json格式,通用类型如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 列表格式 { "code": 1, "message": "成功", "timestamp": 1469690668544, "data": [ ] }
对象格式 { "code": 1, "message": "成功", "timestamp": 1469690668544, "data": {}
}
|
ApiResponse定义如下
1 2 3 4 5 6 7 8 9 10 11 12
| public class ApiResponse<T> {
@SerializedName("code") private int code;
@SerializedName("message") private String message;
@SerializedName("data") private T data; }
|
当返回数组时,对应模型为ApiResponse<List>
当返回对象时,对应模型为ApiResponse
当拿到JSON字符串后,如何根据泛型T解析成ApiResponse格式,我是采用Gson,
但是解析成ApiResponse<List>样式时,遇到一些小曲折,后面我会专门写博客说这个
总结: ApiResponse就是对应接口格式的数据模型对象
Api模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| //定义接口 public interface CommonApi { String URL_GET_BASETYPE = "system/getBaseType";//获取基础数据接口
/** * 获取基础数据 * * @param requestParams * @return */ Observable<ApiResponse<BasicEntity>> getBasicData(RequestParams requestParams);
}
|
Api实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| //实现类 public class CommonApiImpl implements CommonApi { //httpClient是封装的Http请求对象,返回解析成功的Json对象 HttpClient httpClient;
private CommonApiImpl() { this.httpClient = HttpClient.getInstance(); }
private static CommonApi INSTNACE; //单例 public static CommonApi getInstance() { if (INSTNACE == null) { INSTNACE = new CommonApiImpl(); } return INSTNACE; }
@Override public Observable<ApiResponse<BasicEntity>> getBasicData(RequestParams requestParams) { return httpClient.createSimpleRequest(Method.GET, Env.getBaseURL(), URL_GET_BASETYPE, requestParams, BasicEntity.class, "getBasicData"); }
|
Cache模块
Cache是针对某个接口编写Cache示例,Cache永远的针对接口的,下面是BasicDataCache
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public interface BasicDataCache {
/** * 获取缓存信息 * * @return */ Observable<ApiResponse<BasicEntity>> get();
/** * 存储缓存信息 * * @param basicEntityApiResponse */ void put(ApiResponse<BasicEntity> basicEntityApiResponse);
/** * 缓存是否存在 * * @return */ boolean isCache();
/** * 缓存是否过期 * * @return */ boolean isExpired(); }
|
BasicDataCache实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| public class BasicDataCacheImpl implements BasicDataCache { private static final String CACHE_SETTINGS_FILE_NAME = "cacheSetting"; private static final String BASIC_LAST_CACHE_UPDATE = "basic_last_cache_update"; private static final long EXPIRATION_TIME = 60 * 10 * 1000; //h private final File cacheDir; private final FileManager fileManager; private static final String DEFAULT_FILE_NAME = "basicData"; private final Context context;
public BasicDataCacheImpl(Context context) { this.context = context; cacheDir = context.getCacheDir(); this.fileManager = new FileManager(); }
@Override public Observable<ApiResponse<BasicEntity>> get() { return Observable.create(new Observable.OnSubscribe<ApiResponse<BasicEntity>>() { @Override public void call(Subscriber<? super ApiResponse<BasicEntity>> subscriber) { File file = buildFile(); String content = fileManager.readFileContent(file); ApiResponse<BasicEntity> basicDataResponse = JsonSerializer.deserializeRestResponse(content, BasicEntity.class); if (basicDataResponse != null) { if (!subscriber.isUnsubscribed()) subscriber.onNext(basicDataResponse); if (!subscriber.isUnsubscribed()) subscriber.onCompleted(); } else { if (!subscriber.isUnsubscribed()) subscriber.onError(new NullPointerException()); } } }); }
@Override public void put(ApiResponse<BasicEntity> basicEntityApiResponse) { if (basicEntityApiResponse != null) { File basicDataFile = this.buildFile(); String jsonStr = JsonSerializer.serializeRestResponse(basicEntityApiResponse); this.fileManager.writeToFile(basicDataFile, jsonStr); } }
@Override public boolean isCache() { File userInfoFile = this.buildFile(); return this.fileManager.exists(userInfoFile); }
@Override public boolean isExpired() { long currentTime = System.currentTimeMillis(); File file = buildFile(); boolean expired = ((currentTime - file.lastModified) > EXPIRATION_TIME); File file = buildFile(); //如果过期,删除缓存 if (expired) { fileManager.clearFile(file); } return expired; }
private File buildFile() { StringBuilder fileNameBuilder = new StringBuilder(); fileNameBuilder.append(this.cacheDir.getPath()); fileNameBuilder.append(File.separator); fileNameBuilder.append(DEFAULT_FILE_NAME); return new File(fileNameBuilder.toString()); } }
|
Repository层
1 2 3 4 5 6 7 8 9 10 11 12
| public interface CommonRepository {
/** * 获取基础信息 * * @return */ Observable<ApiResponse<BasicEntity>> getBasicData();
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public class CommonRepositoryImpl implements CommonRepository {
private CommonApi commonApi; private BasicDataCache basicDataCache;
@Override public Observable<ApiResponse<BasicEntity>> getBasicData() { Observable<ApiResponse<BasicEntity>> observable; //如果Cache存在且没有过期,取Cache if (basicDataCache != null && basicDataCache.isCache() && !basicDataCache.isExpired()) { observable =basicDataCache.get(); } //否则,取网络 else { observable = getBasicDataRemote(); } RxJava统一调度 return RxScheduler.scheduler(observable); }
//取网络 private Observable<ApiResponse<BasicEntity>> getBasicDataRemote() { return buildBasicDataFromNet.doOnNext(new Action1<ApiResponse<BasicEntity>>() { @Override public void call(ApiResponse<BasicEntity> basicEntityApiResponse) { //如果数据格式正确 if (basicEntityApiResponse.getCode() == 1 && basicEntityApiResponse.getFirstData() != null) { 覆盖Cache basicDataCache.put(basicEntityApiResponse); } } }); }
//构造网络数据 public Observable<ApiResponse<BasicEntity>> buildBasicDataFromNet() { //构造请求参数 RequestParams requestParams = RequestParams.create(); return commonApi.getBasicData(requestParams); }
}
|