Android 网络层Library设计

网络层Library是App最常用的库,需要考虑稳定性,后期的扩展性,更换核心网络库后对项目的影响,ZZNet采用okhttp作为核心网络库。

需求描述:

  • 支持HTTP/HTTPS;
  • 请求支持取消;
  • 支持校验器,可用于统一的JSON校验;
  • 支持拦截器,可用于缓存的处理;
  • 支持重试次数和自定义重试规则;
  • 支持文件上传,上传进度,多文件上传,取消上传;
  • 支持文件下载,下载进度,断点续传,取消下载;
  • 自动处理错误描述和详细的错误类型;

架构设计:

架构实现:

ZZNetValidator,请求及响应校验器

可以针对单个请求配置校验器或所有请求配置统一的校验器,校验请求参数或响应数据的合法性等。例如:可以在请求前校验登录状态,在响应后校验服务端返回的token是否合法,是否需要重新登录等,可以在此处做统一的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 请求及响应校验器
*/
public interface ZZNetValidator {
/**
* 校验参数合法性,运行在主线程
* @param zzNet
* @param paramsSource
* @return
*/
ZZNetResponse isCorrectWithParamsDataRunOnMainThread(@NonNull ZZNet zzNet
, @NonNull Map<String, String> paramsSource);
/**
* 校验API响应合法性,运行在工作线程
* @param zzNet
* @param response
*/
ZZNetResponse isCorrectWithCallBackDataRunOnWorkThread(@NonNull ZZNet zzNet
, @NonNull ZZNetResponse response);
}

ZZNetInterceptor,请求及响应拦截器

针对请求生命周期的各个阶段进行拦截,增强网络库的扩展性。例如:随着业务的发展,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
25
26
27
28
/**
* 请求及响应拦截器
*/
public interface ZZNetInterceptor {
/**
* 通过所有校验,发起网络请求前执行,运行在工作线程。
* @param zzNet
* @return
*/
@WorkerThread
boolean beforeSendNetRequestRunOnWorkThread(@NonNull ZZNet zzNet);
/**
* 发送网络请求后执行,运行在工作线程。
* @param zzNet
*/
@WorkerThread
void afterSendNetRequestRunOnWorkThread(@NonNull ZZNet zzNet);
/**
* 在callback的onDidCompleted方法前执行,运行在主线程
* @param zzNet
* @param response
*/
@MainThread
void beforeDidCompletedRunOnMainThread(@NonNull ZZNet zzNet, @NonNull ZZNetResponse response);
}

ZZNetProcessor,网络请求及响应核心处理器

网络请求处理的抽象接口,把网络请求过程拆分为处理请求和处理响应,可以根据业务需要添加实现;目前有3个实现类,ZZNetStringProcessor(处理字符串类型的响应数据)、 ZZNetUploadProcessor(处理文件上传)、 ZZNetDownloadProcessor(处理文件下载)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@WorkerThread
public interface ZZNetProcessor {
/**
* 处理请求参数
* @param requestBuilder
*/
@WorkerThread
void handleRequestParams(@NonNull Request.Builder requestBuilder);
/**
* 处理请求响应
* @param response
* @param responseEntity
*/
@WorkerThread
void handleResponse(@Nullable Response response, @NonNull ZZNetResponse responseEntity);

ZZNetStringProcessor,字符串处理器

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
/**
* 字符串处理器
*/
@WorkerThread
public final class ZZNetStringProcessor extends ZZNetDefaultProcessor {
public ZZNetStringProcessor(ZZNet zzNet){
super(zzNet);
}
/**
* 处理String类型的响应
* @param response
* @param netResponse
*/
@Override
@WorkerThread
public void handleResponse(Response response, ZZNetResponse netResponse) {
try{
if (response != null) {
if (response.isSuccessful()) {
netResponse.rawString = response.body().string();
if (checkJsonValid(netResponse)){
netResponse.errorType = ZZNetErrorType.Success;
// validator校验响应结果
ZZNetValidator appValidator = zzNet.getBuilder().getAppValidator();
if (appValidator != null){
appValidator.isCorrectWithCallBackDataRunOnWorkThread(zzNet, netResponse);
}
if (zzNet.getValidator() != null){
zzNet.getValidator().isCorrectWithCallBackDataRunOnWorkThread(zzNet, netResponse);
}
}
} else {
netResponse.errorType = ZZNetErrorType.ServerError;
netResponse.errorMsg = zzNet.getAppContext().getString(R.string.lib_network_net_msg4);
}
} else {
netResponse.errorType = ZZNetErrorType.NoResponse;
netResponse.errorMsg = zzNet.getAppContext().getString(R.string.lib_network_net_msg6);
}
}catch (Exception e){
e.printStackTrace();
netResponse.errorType = ZZNetErrorType.Timeout;
netResponse.errorMsg = zzNet.getAppContext().getString(R.string.lib_network_net_msg10);
}
}
/**
* 检查json合法性,并解析json中错误码及错误描述,决定json是否可解析
*
* @param responseEntity 响应实体数据,需先设置responseEntity.rawJson
* @return
*/
private boolean checkJsonValid(@NonNull ZZNetResponse responseEntity) {
if (TextUtils.isEmpty(responseEntity.rawString)) {
responseEntity.errorType = ZZNetErrorType.JSONInValid;
responseEntity.errorMsg = zzNet.getAppContext().getString(R.string.lib_network_net_msg7);
return false;
}
if (responseEntity.rawString.startsWith("\ufeff")) {
responseEntity.rawString = responseEntity.rawString.substring(1);
}
// parse error code
return true;
}
}

ZZNetUploadProcessor,文件上传处理器

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
74
75
76
/**
* 文件上传处理器
*/
@WorkerThread
public class ZZNetUploadProcessor extends ZZNetDefaultProcessor {
public ZZNetUploadProcessor(@NonNull ZZNet zzNet){
super(zzNet);
}
@Override
public void handleRequestParams(Request.Builder requestBuilder) {
super.handleRequestParams(requestBuilder);
handleUploadRequestParams(requestBuilder);
}
private void handleUploadRequestParams(Request.Builder requestBuilder){
if (zzNet.getUploadFile() != null && zzNet.getUploadFile().exists()){
if (zzNet.getMediaType() == null){
throw new NullPointerException("上传文件类型不能为空");
}
RequestBody rawRequestBody = RequestBody.create(MediaType.parse(zzNet.getMediaType())
, zzNet.getUploadFile());
if (zzNet.getMultipartFileKeyName() != null){
MultipartBody multipartBody = new MultipartBody.Builder()
.addFormDataPart(zzNet.getMultipartFileKeyName()
,zzNet.getUploadFile().getName(), rawRequestBody).build();
RequestBody requestBody = new ZZProgressRequest(multipartBody, zzNet.getFileCallback());
requestBuilder.post(requestBody);
}else{
RequestBody requestBody = new ZZProgressRequest(rawRequestBody, zzNet.getFileCallback());
requestBuilder.post(requestBody);
}
}
}
@Override
@WorkerThread
public void handleResponse(@NonNull Response response, @NonNull ZZNetResponse netResponse) {
if (response == null){
netResponse.errorType = ZZNetErrorType.NoResponse;
netResponse.errorMsg = zzNet.getAppContext().getString(R.string.lib_network_net_msg6);
return;
}
if (!response.isSuccessful()){
return;
}
// 解析响应数据
try {
netResponse.rawString = response.body().string();
netResponse.errorType = ZZNetErrorType.Success;
}catch (Exception e){
e.printStackTrace();
}
// validator校验响应
ZZNetValidator appValidator = zzNet.getBuilder().getAppValidator();
if (appValidator != null){
appValidator.isCorrectWithCallBackDataRunOnWorkThread(zzNet, netResponse);
if (netResponse.errorType != ZZNetErrorType.Success){
LogUtils.d(zzNet.getUrl(), "appValidator校验响应不通过,终止");
return;
}
}
if (zzNet.getValidator() != null){
zzNet.getValidator().isCorrectWithCallBackDataRunOnWorkThread(zzNet, netResponse);
if (netResponse.errorType != ZZNetErrorType.Success){
LogUtils.d(zzNet.getUrl(), "apiValidator校验响应不通过,终止");
return;
}
}
}
}

ZZNetDownloadProcessor,文件下载处理器

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/**
* 文件下载处理器
*/
@WorkerThread
public final class ZZNetDownloadProcessor extends ZZNetDefaultProcessor {
private static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 KB
public ZZNetDownloadProcessor(ZZNet zzNet){
super(zzNet);
}
@Override
@WorkerThread
public void handleRequestParams(Request.Builder requestBuilder) {
super.handleRequestParams(requestBuilder);
handleDownloadRequestParams(requestBuilder);
}
/**
* 处理下载请求参数
* @param requestBuilder
*/
@WorkerThread
private void handleDownloadRequestParams(Request.Builder requestBuilder){
if (canAddRangeHeader()){
// 断点续传下载
requestBuilder.header("RANGE", "bytes=" + zzNet.getSaveFile().length() + "-");
}
}
/**
* 能否拼接断点续传文件Range
* @return
*/
@WorkerThread
private boolean canAddRangeHeader(){
File saveOrUploadFile = zzNet.getSaveFile();
if (zzNet.isSupportResumeDownload()
&& zzNet.getRequestModel() == ZZNetRequestModel.DownloadFile
&& saveOrUploadFile != null && saveOrUploadFile.exists()
&& saveOrUploadFile.length() > 0) {
return true;
}
return false;
}
/**
* 处理文件下载响应
* @param response
* @throws IOException
*/
@Override
@WorkerThread
public void handleResponse(@Nullable Response response, @NonNull ZZNetResponse netResponse){
if (response == null){
netResponse.errorType = ZZNetErrorType.NoResponse;
netResponse.errorMsg = zzNet.getAppContext().getString(R.string.lib_network_net_msg6);
return;
}
if (response.code() == 416){
LogUtils.w(zzNet.getUrl(), "文件已经下载完毕,不需要再次下载,终止本次请求");
netResponse.errorType = ZZNetErrorType.Success;
return;
}
if (!response.isSuccessful()){
return;
}
// validator校验响应
ZZNetValidator appValidator = zzNet.getBuilder().getAppValidator();
if (appValidator != null){
appValidator.isCorrectWithCallBackDataRunOnWorkThread(zzNet, netResponse);
if (netResponse.errorType != ZZNetErrorType.Success){
LogUtils.d(zzNet.getUrl(), "appValidator校验响应不通过,终止");
return;
}
}
if (zzNet.getValidator() != null){
zzNet.getValidator().isCorrectWithCallBackDataRunOnWorkThread(zzNet, netResponse);
if (netResponse.errorType != ZZNetErrorType.Success){
LogUtils.d(zzNet.getUrl(), "apiValidator校验响应不通过,终止");
return;
}
}
boolean isSupportRange = isSupportRange(response);
File saveOrUploadFile = zzNet.getSaveFile();
final boolean append = saveOrUploadFile.length() > 0 && isSupportRange;
if (LogUtils.DEBUG){
LogUtils.d(zzNet.getUrl(), "文件总大小", String.valueOf(response.body().contentLength())
, String.format("当前是否断点续传%s", Boolean.valueOf(append)));
}
OutputStream os = null;
try {
os = new FileOutputStream(saveOrUploadFile, append);
}catch (FileNotFoundException e){
e.printStackTrace();
try {
os.close();
}catch (IOException ex){
ex.printStackTrace();
}
return;
}
InputStream is = response.body().byteStream();
boolean downloadSuccess = true;
boolean isCanceled = false;
try{
isCanceled = !copyStream(response.body().contentLength(), is, os, isSupportRange);
}catch (Exception e){
e.printStackTrace();
downloadSuccess = false;
}finally {
try{
if (os != null){
os.close();
}
if (is != null) {
is.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
if (isCanceled){
// 被用户取消
netResponse.errorType = ZZNetErrorType.Canceled;
}else if (downloadSuccess){
netResponse.errorType = ZZNetErrorType.Success;
}
}
/**
* 是否支持断点上传
* @param response
* @return
*/
@WorkerThread
public boolean isSupportRange(Response response) {
if (response == null || !zzNet.isSupportResumeDownload()) return false;
if ("bytes".equals(response.header("Accept-Ranges"))) return true;
String contentRanges = response.header("Content-Range");
if (contentRanges != null && contentRanges.startsWith("bytes")) return true;
return false;
}
/**
* 写数据到文件
* @param total
* @param is
* @param os
* @return
* @throws IOException
*/
@WorkerThread
public boolean copyStream(long total, InputStream is, OutputStream os, boolean isSupportRange) throws IOException {
long current = 0;
if (total <= 0) {
total = is.available();
}
long oldFileCount = 0;
File saveOrUploadFile = zzNet.getSaveFile();
if (isSupportRange && saveOrUploadFile != null && saveOrUploadFile.exists() && saveOrUploadFile.length() > 0) {
oldFileCount = saveOrUploadFile.length();
total += oldFileCount;
}
final byte[] bytes = new byte[DEFAULT_BUFFER_SIZE];
int count;
while ((count = is.read(bytes, 0, DEFAULT_BUFFER_SIZE)) != -1) {
if (zzNet.isCanceled()){
return false;
}
os.write(bytes, 0, count);
current += count;
if (zzNet.getFileCallback() != null){
ZZNetFileCallback callback = zzNet.getFileCallback().get();
if (callback != null){
callback.onProgess(current + oldFileCount, total);
}
}
}
return true;
}
}

自定义重试规则

允许请求失败后的重试操作,可以自定义重试规则,重试次数等。例如:客户端支付成功后需要查询支付状态,服务端的支付状态依赖第三方支付平台的通知,为降低通知延迟对客户端的影响,可以自定义重试规则,当支付失败时多重试几次。

重试机制规则抽象接口

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 重试机制规则
*/
public interface ZZNetRetryRule {
/**
* 是否需要重试,可在此定义重试规则,注意此函数是运行在工作线程中
* @param zzNetResponse
* @return
*/
@WorkerThread
boolean needRetry(ZZNetResponse zzNetResponse);
}

重试机制具体使用方法

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
/**
* 自定义重试规则,App端支付成功后查询支付状态
* ,如果查询结果为支付失败,等待500ms后继续查询(最多3次)。
*/
public static class QueryPayStatusRetryRule implements ZZNetRetryRule {
// 该函数运行在工作线程中
@Override
public boolean needRetry(ZZNetResponse response) {
// 可根据zzNetResponse定义重试规则
boolean needRetry = true;
if (!TextUtils.isEmpty(response.rawString)){
try {
final JSONObject jsonObject = JSON.parseObject(response.rawString);
if (jsonObject != null){
final boolean paySuccess = jsonObject.getBooleanValue("xxx");
if (!paySuccess){
if (LogUtils.DEBUG){
LogUtils.w("支付失败,延迟500ms重新查询");
}
SystemClock.sleep(500);
}else{
// 支付成功, 不需要重试
needRetry = false;
}
}
}catch (Exception e){
e.printStackTrace();
}
}
return needRetry;
}
}
0%