移动架构之网络访问框架设计

这里通过设计并实现一个网络框架的搭建,下载

设计思路

支持请求JSON文本类型,这样的话需要一个处理JSON的类,这里使用fastjson依赖
发出请求时,调用层不用关心上传参数,这里就需要对请求进行封装,并且将其封装的参数传递给框架,由框架去处理
获取数据后在框架内完成json数据的解析
回调时,调用层只需传入json的对应响应类,回调响应结果给主线程
并且需要支持高并发请求,使用线程池和人物列队

JSON数据访问解析实现步骤

首先定义三个接口,用于获取网络,处理数据和调用层回调
获取网络主要涉及到设置url,获取网络,监听接口,设置请求参数

1
2
3
4
5
6
7
8
9
10
11
//获取网络
public interface IHttpService {
//设置url
void setUrl(String url);
//执行获取网络
void excute();
//设置处理接口
void setHttpListener(IHttpListener httpListener);
//设置请求参数
void setRequestData(byte[] requestData);
}

处理数据会有成功和失败

1
2
3
4
5
6
//处理结果
public interface IHttpListener {
//处理结果 回调
void onSuccess(HttpEntity httpEntity);
void onFail();
}

回调与数据处理结果相统一

1
2
3
4
5
6
//回调
public interface IDataListener<M> {
//回调结果给调用层
void onSuccess(M m);
void onFail();
}

然后实现以上接口,完成网络的获取和数据的处理
完成网络的获取,在调用的时候完成相应的参数设置

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
public class JSONHttpService implements IHttpService {

private IHttpListener httpListener;
private HttpClient httpClient = new DefaultHttpClient();
private HttpPost httpPost;
private String url;
private byte[] requestData;
//httpClient获取网络回调
private HttpResponseHandler httpResponseHandler = new HttpResponseHandler();

@Override
public void setUrl(String url) {
this.url = url;
}

@Override
public void excute() {
httpPost = new HttpPost(url);
ByteArrayEntity byteArrayEntity = new ByteArrayEntity(requestData);
httpPost.setEntity(byteArrayEntity);
try {
httpClient.execute(httpPost,httpResponseHandler);
} catch (IOException e) {
httpListener.onFail();
e.printStackTrace();
}
}

@Override
public void setHttpListener(IHttpListener httpListener) {
this.httpListener = httpListener;
}

@Override
public void setRequestData(byte[] requestData) {
this.requestData = requestData;
}

private class HttpResponseHandler extends BasicResponseHandler{
@Override
public String handleResponse(HttpResponse response) throws IOException {
//响应码
int code = response.getStatusLine().getStatusCode();
if(code == 200){
httpListener.onSuccess(response.getEntity());
}else {
httpListener.onFail();
}
return super.handleResponse(response);
}
}
}

完成数据的处理,这里需要注意一点的是响应类的传入以及通知主线程的过程

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
public class JSONDealListener<M> implements IHttpListener {

private Class<M> response;
private IDataListener<M> dataListener;

Handler handler = new Handler(Looper.getMainLooper());

public JSONDealListener(Class<M> response, IDataListener<M> dataListener) {
this.response = response;
this.dataListener = dataListener;
}

@Override
public void onSuccess(HttpEntity httpEntity) {
InputStream inputStream = null;
try {
inputStream = httpEntity.getContent();
//得到网络返回的数据
String content = getContent(inputStream);
final M m = JSON.parseObject(content, response);
handler.post(new Runnable() {
@Override
public void run() {
dataListener.onSuccess(m);
}
});
} catch (IOException e) {
dataListener.onFail();
}
}

private String getContent(InputStream inputStream) {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} catch (IOException e) {
dataListener.onFail();
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
dataListener.onFail();
e.printStackTrace();
}
}
return sb.toString();
}

@Override
public void onFail() {
dataListener.onFail();
}
}

封装请求体,使调用更加方便

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
public class RequestHolder<T> {
//执行下载类
private IHttpService httpService;
//获取数据,回调结果类
private IHttpListener httpListener;
//请求参数对应的实体
private T requestInfo;
//链接
private String url;

public IHttpService getHttpService() {
return httpService;
}

public void setHttpService(IHttpService httpService) {
this.httpService = httpService;
}

public IHttpListener getHttpListener() {
return httpListener;
}

public void setHttpListener(IHttpListener httpListener) {
this.httpListener = httpListener;
}

public T getRequestInfo() {
return requestInfo;
}

public void setRequestInfo(T requestInfo) {
this.requestInfo = requestInfo;
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}
}

之后就是设计线程池,用来执行任务,需要建立子线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class HttpTask<T> implements Runnable{

private IHttpService httpService;
public HttpTask(RequestHolder<T> requestHolder){
httpService = requestHolder.getHttpService();
httpService.setHttpListener(requestHolder.getHttpListener());
httpService.setUrl(requestHolder.getUrl());
T request = requestHolder.getRequestInfo();
String requestInfo = JSON.toJSONString(request);
try {
httpService.setRequestData(requestInfo.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}

@Override
public void run() {
httpService.excute();
}
}

实现任务调度池和线程池的创建,这里仿照的使AsyncTask里面使用的线程池

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
public class ThreadPoolManager {
private static final String TAG = "cj5785";
private static ThreadPoolManager instance = new ThreadPoolManager();
//定义线程池
private ThreadPoolExecutor threadPoolExecutor;
//线程池参数初始化
private LinkedBlockingQueue<Future<?>> taskQueue = new LinkedBlockingQueue<>();
private RejectedExecutionHandler handler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
taskQueue.put(new FutureTask<Object>(r, null));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};

//创建线程池
private ThreadPoolManager() {
threadPoolExecutor = new ThreadPoolExecutor(4, 10, 10,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4), handler);
threadPoolExecutor.execute(runnable);
}

//添加执行任务
public <T> void execute(FutureTask<T> futureTask) throws InterruptedException {
taskQueue.put(futureTask);
}

private Runnable runnable = new Runnable() {
@Override
public void run() {
while (true) {
FutureTask futureTask = null;
try {
//阻塞式方法
futureTask = (FutureTask) taskQueue.take();
Log.d(TAG, "任务数量:" + taskQueue.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
if (futureTask != null) {
threadPoolExecutor.execute(futureTask);
Log.d(TAG, "线程池大小:" + threadPoolExecutor.getPoolSize());
}
}
}
};

public static ThreadPoolManager getInstance() {
return instance;
}
}

最后建立调用层与框架层的联系关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Volley {
public static <T, M> void sendRequest(T request, String url, Class<M> response, IDataListener dataListener) {
RequestHolder<T> requestHolder = new RequestHolder<>();
requestHolder.setUrl(url);
IHttpService httpService = new JSONHttpService();
IHttpListener httpListener = new JSONDealListener<>(response,dataListener);
requestHolder.setHttpService(httpService);
requestHolder.setHttpListener(httpListener);
HttpTask<T> httpTask = new HttpTask<>(requestHolder);
try {
ThreadPoolManager.getInstance().execute(new FutureTask<Object>(httpTask,null));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

然后是个Bean类,用作响应类,相应类是根据json数据的格式封装而成
测试网站的数据为:

1
{"code":0,"name":"jack","time":"2018-01-01","user_id":"10086"}

对应的Bean文件为

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
class LoginResponse
{
private int code;
private String user_id;
private String time;
private String name;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getUser_id() {
return user_id;
}
public void setUser_id(String user_id) {
this.user_id = user_id;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}


@Override
public String toString() {
return "LoginResponse{" +
"code=" + code +
", user_id='" + user_id + '\'' +
", time='" + time + '\'' +
", name='" + name + '\'' +
'}';
}
}

最后在activity中进行调用

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
public class MainActivity extends AppCompatActivity {

private static final String TAG = "cj5785";
private static final String url = "http://192.168.1.2:8080/test/";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void onStart(View view) {
for (int i = 0; i < 50; i++) {
Volley.sendRequest(null, url, LoginResponse.class, new IDataListener<LoginResponse>() {
@Override
public void onSuccess(LoginResponse loginRespense) {
Log.d(TAG, loginRespense.toString());
}

@Override
public void onFail() {
Log.d(TAG, "获取失败");
}
});
}
}
}

注意在清单文件中标明权限
这里导入了两个依赖,和一个库,在gradle中声明

1
2
3
4
5
6
7
8
9
10
android {
···
useLibrary 'org.apache.http.legacy'
}

dependencies {
···
implementation 'com.alibaba:fastjson:1.+'
implementation 'httpcomponents-httpcore:httpcore:4.+'
}

这样就完成了json数据的访问和解析,接下来就是逐步完成其功能

完成下载功能

下载回调监听接口

1
2
3
4
5
6
7
8
public interface IDownloadServiceCallable {
void onDownloadStatusChanged(DownloadItemInfo downloadItemInfo);
void onTotalLengthReceived(DownloadItemInfo downloadItemInfo);
void onCurrentSizeChanged(DownloadItemInfo downloadItemInfo,double downLength,long speed);
void onDownloadSuccess(DownloadItemInfo downloadItemInfo);
void onDownloadPause(DownloadItemInfo downloadItemInfo);
void onDownloadError(DownloadItemInfo downloadItemInfo,int var2,String var3);
}

下载文件信息

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
public class DownloadItemInfo extends BaseEntity<DownloadItemInfo>{
private long currentLength;
private long totalLength;
private String url;
private String filePath;
private transient HttpTask httpTask;
private DownloadStatus status;

public DownloadItemInfo() {}

public DownloadItemInfo(String url, String filePath) {
this.url = url;
this.filePath = filePath;
}

public long getCurrentLength() {
return currentLength;
}

public void setCurrentLength(long currentLength) {
this.currentLength = currentLength;
}

public long getTotalLength() {
return totalLength;
}

public void setTotalLength(long totalLength) {
this.totalLength = totalLength;
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

public String getFilePath() {
return filePath;
}

public void setFilePath(String filePath) {
this.filePath = filePath;
}

public HttpTask getHttpTask() {
return httpTask;
}

public void setHttpTask(HttpTask httpTask) {
this.httpTask = httpTask;
}

public DownloadStatus getStatus() {
return status;
}

public void setStatus(DownloadStatus status) {
this.status = status;
}
}

下载状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public enum DownloadStatus {
waitting(0),
starting(1),
downloading(2),
pause(3),
finish(4),
failed(5);
private int value;

DownloadStatus(int value) {
this.value = value;
}

public int getValue() {
return value;
}

public void setValue(int value) {
this.value = value;
}
}

实现IHttpListener接口

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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
public class DownLoadListener implements IHttpListener {

private static final String TAG = "cj5785";
//下载信息
private DownloadItemInfo downloadItemInfo;
//文件信息
private File file;
//链接url
protected String url;
//断点位置
private long breakPoint;
//回调方法
private IDownloadServiceCallable downloadServiceCallable;
//获取网络
private IHttpService httpService;
//获取主线程
private Handler handler = new Handler(Looper.getMainLooper());

public DownLoadListener(DownloadItemInfo downloadItemInfo) {
this.downloadItemInfo = downloadItemInfo;
}

public DownLoadListener(DownloadItemInfo downloadItemInfo,
IDownloadServiceCallable downloadServiceCallable,
IHttpService httpService) {
this.downloadItemInfo = downloadItemInfo;
this.downloadServiceCallable = downloadServiceCallable;
this.httpService = httpService;
this.file = new File(downloadItemInfo.getFilePath());
//得到已下载长度
this.breakPoint = file.length();
}

public void addHttpHeader(Map<String, String> headerMap) {
long length = getFile().length();
if (length > 0L) {
headerMap.put("RANGE", "bytes=" + length + "-");
}
}

@Override
public void onSuccess(HttpEntity httpEntity) {
Log.d(TAG, "onSuccess");
InputStream inputStream = null;
try {
inputStream = httpEntity.getContent();
} catch (IOException e) {
e.printStackTrace();
}
//开始时间
long startTime = System.currentTimeMillis();
//下载速度
long speed = 0L;
//花费时间
long useTime = 0L;
//下载长度
long getLen = 0L;
//接收的长度
long receiveLen = 0L;
//已下载的长度
long dataLength = httpEntity.getContentLength();
Log.d(TAG, "onSuccess: dataLength = " + dataLength);
//单位时间下载字节数
long calcSpeedLen = 0L;
//总长度
long totalLength = this.breakPoint + dataLength;
Log.d(TAG, "onSuccess: totalLength = " + totalLength);
//更新数量
receviceTotalLength(totalLength);
//更新状态
downloadStatusChange(DownloadStatus.downloading);
byte[] buffer = new byte[1024];
int count = 0;
long currentTime = System.currentTimeMillis();
BufferedOutputStream bos = null;
FileOutputStream fos = null;
try {
if (!makeDir(getFile().getParentFile())) {
downloadServiceCallable.onDownloadError(downloadItemInfo, 0, "创建文件夹失败");
} else {
fos = new FileOutputStream(getFile(), true);
bos = new BufferedOutputStream(fos);
int length = 1;
while ((length = inputStream.read(buffer)) != -1) {
if (getHttpService().isCancle()) {
downloadServiceCallable.onDownloadError(downloadItemInfo, 1, "用户取消了");
return;
}
if (getHttpService().isPause()) {
downloadServiceCallable.onDownloadError(downloadItemInfo, 2, "用户暂停了");
return;
}
bos.write(buffer, 0, length);
getLen += (long) length;
receiveLen += (long) length;
calcSpeedLen += (long) length;
++count;
if (receiveLen * 10L / totalLength >= 1L || count >= 5000) {
currentTime = System.currentTimeMillis();
useTime = currentTime - startTime;
startTime = currentTime;
speed = 1000L * calcSpeedLen / useTime;
count = 0;
calcSpeedLen = 0L;
receiveLen = 0L;
downloadLengthChange(breakPoint + getLen, totalLength, speed);
}
}
bos.close();
inputStream.close();
if (dataLength != getLen) {
downloadServiceCallable.onDownloadError(downloadItemInfo, 3, "下载长度不相等");
} else {
downloadLengthChange(breakPoint + getLen, totalLength, speed);
downloadServiceCallable.onDownloadSuccess(downloadItemInfo.copy());
}
}
} catch (IOException e) {
if (getHttpService() != null) {
// getHttpService().abortRequest();
}
return;
} catch (Exception e) {
if (getHttpService() != null) {
// getHttpService().abortRequest();
}
} finally {
try {
if (bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (httpEntity != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

//创建父文件夹
private boolean makeDir(File parentFile) {
// Log.d(TAG, "makeDir");
return parentFile.exists() && !parentFile.isFile()
? parentFile.exists() && parentFile.isDirectory()
: parentFile.mkdirs();
}

//下载长度变化
private void downloadLengthChange(final long downLength, final long totalLength, final long speed) {
// Log.d(TAG, "downloadLengthChange");
downloadItemInfo.setCurrentLength(downLength);
final DownloadItemInfo copyDownloadItemInfo = downloadItemInfo.copy();
if (downloadServiceCallable != null) {
synchronized (downloadServiceCallable) {
handler.post(new Runnable() {
@Override
public void run() {
downloadServiceCallable.onCurrentSizeChanged(copyDownloadItemInfo, (double) downLength / totalLength, speed);
}
});
}
}
}

//得到文件
private File getFile() {
return file;
}

//获取HttpService对象
private IHttpService getHttpService() {
return httpService;
}

//状态回调
private void downloadStatusChange(DownloadStatus downloading) {
// Log.d(TAG, "downloadStatusChange");
downloadItemInfo.setStatus(downloading);
final DownloadItemInfo copyDownloadItemInfo = downloadItemInfo.copy();
if (downloadServiceCallable != null) {
synchronized (downloadServiceCallable) {
handler.post(new Runnable() {
@Override
public void run() {
downloadServiceCallable.onDownloadStatusChanged(copyDownloadItemInfo);
}
});
}
}
}

//更新下载状态
private void receviceTotalLength(long totalLength) {
// Log.d(TAG, "receviceTotalLength");
downloadItemInfo.setCurrentLength(totalLength);
final DownloadItemInfo copyDownloadItemInfo = downloadItemInfo.copy();
if (downloadServiceCallable != null) {
synchronized (downloadServiceCallable) {
handler.post(new Runnable() {
@Override
public void run() {
downloadServiceCallable.onTotalLengthReceived(copyDownloadItemInfo);
}
});
}
}
}

@Override
public void onFail() {

}
}

在回调时侯使用克隆对象

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
//克隆对象,防止修改
public class BaseEntity<T> implements Serializable{
private static final long serialVersionUID = 1L;
public BaseEntity(){}
public T copy(){
ByteArrayOutputStream byteArrayOutputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
byteArrayOutputStream = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
ByteArrayInputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Object result= objectInputStream.readObject();
return (T) result;
} catch (IOException io) {
io.printStackTrace();
} catch (ClassNotFoundException classNot) {
classNot.printStackTrace();
} finally {
if(byteArrayOutputStream != null) {
try {
byteArrayOutputStream.close();
} catch (IOException io) {
io.printStackTrace();
}
}

if(objectOutputStream != null) {
try {
objectOutputStream.close();
} catch (IOException io) {
io.printStackTrace();
}
}

}
return null;
}
}

完成下载请求

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
public class FileDownHttpService implements IHttpService {
private static final String TAG = "cj5785";
private HttpClient httpClient = new DefaultHttpClient();
private HttpGet httpGet;
private byte[] requestData;
private String url;
//请求头信息,保证线程安全
private Map<String, String> headerMap = Collections.synchronizedMap(new HashMap<String, String>());
//含有请求处理的接口
private IHttpListener httpListener;
//httpClient获取网络回调
private HttpResponseHandler httpResponseHandler = new HttpResponseHandler();

@Override
public void setUrl(String url) {
this.url = url;
}

@Override
public void excute() {
httpGet = new HttpGet(url);
//创建请求头,以方便断点下载
constrcutHeader();
// ByteArrayEntity byteArrayEntity = new ByteArrayEntity(requestData);
// httpGet.setEntity(byteArrayEntity);
try {
httpClient.execute(httpGet, httpResponseHandler);
} catch (IOException e) {
httpListener.onFail();
e.printStackTrace();
}
}


@Override
public void setHttpListener(IHttpListener httpListener) {
this.httpListener = httpListener;
}

@Override
public void setRequestData(byte[] requestData) {
this.requestData = requestData;
}

@Override
public Map<String, String> getHttpHeadMap() {
return headerMap;
}

@Override
public void pause() {

}

@Override
public boolean isPause() {
return false;
}

@Override
public void cancle() {

}

@Override
public boolean isCancle() {
return false;
}

private void constrcutHeader() {
Iterator iterator = headerMap.keySet().iterator();
while (iterator.hasNext()) {
String key = (String) iterator.next();
String value = headerMap.get(key);
Log.d(TAG, "constrcutHeader:{key=" + key + ",value=" + value + "}");
httpGet.addHeader(key, value);
}
}

public Map<String, String> getHeaderMap() {
return headerMap;
}

private class HttpResponseHandler extends BasicResponseHandler {
@Override
public String handleResponse(HttpResponse response) throws IOException {
//响应码
int code = response.getStatusLine().getStatusCode();
if (code == 200 || code == 206) {
httpListener.onSuccess(response.getEntity());
} else {
httpListener.onFail();
}
return null;
}
}
}

编写下载管理,实现下载封装

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
public class DownFileManager implements IDownloadServiceCallable {

private static final String TAG = "cj5785";
//保证线程安全
private byte[] lock = new byte[0];

public void down(String url) {

synchronized (lock) {
String[] preFixs = url.split("/");
String afterFix = preFixs[preFixs.length - 1];
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), afterFix);
DownloadItemInfo downloadItemInfo = new DownloadItemInfo(url, file.getAbsolutePath());
RequestHolder requestHolder = new RequestHolder();
//设置下载策略
IHttpService httpService = new FileDownHttpService();
//得到请求头参数
Map<String, String> map = httpService.getHttpHeadMap();
//处理结果
IHttpListener httpListener = new DownLoadListener(downloadItemInfo, this, httpService);
//组装request
requestHolder.setHttpListener(httpListener);
requestHolder.setHttpService(httpService);
requestHolder.setUrl(url);
//实例化HttpTask
HttpTask httpTask = new HttpTask(requestHolder);
try {
ThreadPoolManager.getInstance().execute(new FutureTask<Object>(httpTask, null));
} catch (InterruptedException e) {
}
}
}

@Override
public void onDownloadStatusChanged(DownloadItemInfo downloadItemInfo) {
Log.d(TAG, "onDownloadStatusChanged");
}

@Override
public void onTotalLengthReceived(DownloadItemInfo downloadItemInfo) {
Log.d(TAG, "onTotalLengthReceived");
}

@Override
public void onCurrentSizeChanged(DownloadItemInfo downloadItemInfo, double downLength, long speed) {
Log.i(TAG, "下载速度: " + speed / 1000 + "k/s");
Log.i(TAG, "路径: " + downloadItemInfo.getFilePath() + ",下载进度: " + downLength + ",速度: " + speed);
}

@Override
public void onDownloadSuccess(DownloadItemInfo downloadItemInfo) {
Log.i(TAG, "下载成功! 路径: " + downloadItemInfo.getFilePath() + ",url: " + downloadItemInfo.getUrl());
}

@Override
public void onDownloadPause(DownloadItemInfo downloadItemInfo) {
Log.d(TAG, "onDownloadPause");
}

@Override
public void onDownloadError(DownloadItemInfo downloadItemInfo, int var2, String var3) {
Log.d(TAG, "onDownloadError " + var3);
}
}

修改HttpTask,使其可以处理文件下载响应

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 class HttpTask<T> implements Runnable {

private static final String TAG = "cj5785";
private IHttpService httpService;

public HttpTask(RequestHolder<T> requestHolder) {
httpService = requestHolder.getHttpService();
httpService.setHttpListener(requestHolder.getHttpListener());
httpService.setUrl(requestHolder.getUrl());
IHttpListener httpListener = requestHolder.getHttpListener();
httpListener.addHttpHeader(httpService.getHttpHeadMap());
try {
T request = requestHolder.getRequestInfo();
if (request != null) {
String requestInfo = JSON.toJSONString(request);
httpService.setRequestData(requestInfo.getBytes("UTF-8"));
}

} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}

@Override
public void run() {
httpService.excute();
}
}

调用下载,完成下载测试

1
2
3
4
public void onDown(View view) {
DownFileManager downFileService = new DownFileManager();
downFileService.down("http://192.168.1.2:8080/test/test.apk");
}

以上,就完成了文件的下载,同时实现了断点续传

结合数据库实现下载管理

下载需要保存的记录

  • 下载id
  • 下载url
  • 下载存储的文件路径filePath
  • 下载文件显示名displayName
  • 下载文件总大小totalLen
  • 下载文件当前大小currentLen
  • 下载开始时间startTime
  • 下载结束时间finishTime
  • 用户userId
  • 下载优先级priority
  • 下载停止模式stopMode
  • 下载的状态status

下载流程图

下载Dao应具备的功能

内部应该含有DownItemInfo list集合:此集合包含着自动暂停,没有完成的下载
比较器Comparator:根据Id排序
查找自动取消的下载集合,方便外层做恢复下载:findAllAutoCancelRecords()
更新文件长度
根据id查找下载记录对象:findRecordById(int recordId)
根据下载地址和下载文件路径查找下载记录:findRecord(String url, String filePath)
根据 下载文件路径查找下载记录
根据id更新文件当前下载大小:updateCurrentLen(int id, Long currentLen)
根据下载id删除下载记录:removeRecord(int id)
根据id从内存中移除下载记录:removeRecordFromMemery(int id)

实现

将之前的数据库框架使用在项目中
DbField.java DbTable.java
BaseDao.java BaseDaoFactory.java IBaseDao.java
添加相应的下载操作数据库类

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
public class DownloadDao extends BaseDao<DownloadItemInfo> {

//保存应该下载的集合
private List<DownloadItemInfo> downloadItemInfoList =
Collections.synchronizedList(new ArrayList<DownloadItemInfo>());

//比较器
private DownloadInfoComparator downloadInfoComparator = new DownloadInfoComparator();

//生成下载ID
private Integer generateRecordId() {
int maxId = 0;
String sql = "select max(id) from " + getTableName();
synchronized (DownloadDao.class) {
Cursor cursor = database.rawQuery(sql, null);
if (cursor.moveToNext()) {
String[] colmName = cursor.getColumnNames();
int index = cursor.getColumnIndex("max(id)");
if (index != -1) {
Object value = cursor.getInt(index);
if (value != null) {
maxId = Integer.parseInt(String.valueOf(value));
}
}
}
}
return maxId + 1;
}

//根据下载地址和下载文件路径查找下载记录
public DownloadItemInfo findRecord(String url, String filePath) {
synchronized (DownloadDao.class) {
for (DownloadItemInfo record : downloadItemInfoList) {
if (record.getUrl().equals(url) && record.getFilePath().equals(filePath)) {
return record;
}
}
//内存集合找不到,就从数据库中查找
DownloadItemInfo where = new DownloadItemInfo();
where.setUrl(url);
where.setFilePath(filePath);
List<DownloadItemInfo> resultList = super.query(where);
if (resultList.size() > 0) {
return resultList.get(0);
}
return null;
}
}

@Override
protected String createTable() {
return "create table if not exists tb_downloadInfo(id Integer primary key,url TEXT not null," +
"filePath TEXT not null,displayName TEXT,status Integer,totalLen Long,currentLen Long," +
"startTime TEXT,finishTime TEXT,userId TEXT,httpTaskType TEXT,priority Integer," +
"stopMode Integer,downloadMaxSizeKey TEXT,unique(filePath))";
}

//查找下载记录
public List<DownloadItemInfo> findRecord(String filePath) {
synchronized (DownloadDao.class) {
DownloadItemInfo where = new DownloadItemInfo();
where.setFilePath(filePath);
List<DownloadItemInfo> resultList = super.query(where);
return resultList;
}
}

//添加下载记录
public int addRecrod(String url, String filePath, String displayName, int priority) {
synchronized (DownloadDao.class) {
DownloadItemInfo existDownloadInfo = findRecord(url, filePath);
if (existDownloadInfo == null) {
DownloadItemInfo record = new DownloadItemInfo();
record.setId(generateRecordId());
record.setUrl(url);
record.setFilePath(filePath);
record.setDisplayName(displayName);
record.setStatus(DownloadStatus.waitting.getValue());
record.setTotalLen(0L);
record.setCurrentLen(0L);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
record.setStartTime(dateFormat.format(new Date()));
record.setFinishTime("0");
record.setPriority(priority);
super.insert(record);
downloadItemInfoList.add(record);
return record.getId();
}
return -1;
}
}

//更新下载记录
public int updateRecord(DownloadItemInfo record) {
DownloadItemInfo where = new DownloadItemInfo();
where.setId(record.getId());
int result = 0;
synchronized (DownloadDao.class) {
try {
result = super.update(record, where);
} catch (Throwable e) {
}
if (result > 0) {
for (int i = 0; i < downloadItemInfoList.size(); i++) {
if (downloadItemInfoList.get(i).getId().intValue() == record.getId()) {
downloadItemInfoList.set(i, record);
break;
}
}
}
}
return result;
}

//根据下载地址和下载文件路径查找下载记录
public DownloadItemInfo findSigleRecord(String filePath) {
List<DownloadItemInfo> downloadInfoList = findRecord(filePath);
if (downloadInfoList.isEmpty()) {
return null;
}
return downloadInfoList.get(0);
}

//根据id查找下载记录对象
public DownloadItemInfo findRecordById(int recordId) {
synchronized (DownloadDao.class) {
for (DownloadItemInfo record : downloadItemInfoList) {
if (record.getId() == recordId) {
return record;
}
}
DownloadItemInfo where = new DownloadItemInfo();
where.setId(recordId);
List<DownloadItemInfo> resultList = super.query(where);
if (resultList.size() > 0) {
return resultList.get(0);
}
return null;
}
}

//根据id从内存中移除下载记录
public boolean removeRecordFromMemery(int id) {
synchronized (DownloadItemInfo.class) {
for (int i = 0; i < downloadItemInfoList.size(); i++) {
if (downloadItemInfoList.get(i).getId() == id) {
downloadItemInfoList.remove(i);
break;
}
}
return true;
}
}

class DownloadInfoComparator implements Comparator<DownloadItemInfo> {

@Override
public int compare(DownloadItemInfo o1, DownloadItemInfo o2) {
return o2.getId() - o1.getId();
}
}
}

策略枚举类

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
public enum Priority {
low(0),
middle(1),
high(2);
private int value;

Priority(int value) {
this.value = value;
}

public void setValue(int value) {
this.value = value;
}

public int getValue() {
return value;
}

public static Priority getInstance(int value) {
for (Priority priority : Priority.values()) {
if (priority.getValue() == value) {
return priority;
}
}
return Priority.middle;
}
}

停止模式枚举类

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
public enum DownloadStopMode {
//后台根据下载优先级调度自动停止下载任务
auto(0),
//手动停止下载任务/
hand(1);
//值
private Integer value;

public void setValue(Integer value) {
this.value = value;
}

public Integer getValue() {
return value;
}

DownloadStopMode(Integer value) {
this.value = value;
}

public static DownloadStopMode getInstance(int value) {
for (DownloadStopMode mode : DownloadStopMode.values()) {
if (mode.getValue() == value) {
return mode;
}
}
return DownloadStopMode.auto;
}
}

修改DownFileManager

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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
public class DownFileManager implements IDownloadServiceCallable {

private static final String TAG = "cj5785";
//保证线程安全
private byte[] lock = new byte[0];
DownloadDao downloadDao = BaseDaoFactory.getInstance().getDataHelper(
DownloadDao.class, DownloadItemInfo.class);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
//观察者模式
private final List<IDownloadCallable> applisteners = new CopyOnWriteArrayList<>();

private static List<DownloadItemInfo> downloadFileTaskList = new CopyOnWriteArrayList();
Handler handler = new Handler(Looper.getMainLooper());

public int download(String url) {
String[] preFixs = url.split("/");
return download(url, Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator + preFixs[preFixs.length - 1],
preFixs[preFixs.length - 1], Priority.middle);
}

public int download(String url, String filePath) {
String[] preFixs = url.split("/");
return download(url, filePath, preFixs[preFixs.length - 1], Priority.middle);
}

public int download(String url, String filePath, String displayName) {
return download(url, filePath, displayName, Priority.middle);
}

public int download(String url, String filePath, String displayName, Priority priority) {
if (priority == null) {
priority = Priority.low;
}
File file = new File(filePath);
DownloadItemInfo downloadItemInfo = null;
downloadItemInfo = downloadDao.findRecord(url, filePath);
//尚未下载
if (downloadItemInfo == null) {
//根据文件路径查找
List<DownloadItemInfo> infoList = downloadDao.findRecord(filePath);
//大于0 表示下载
if (infoList.size() > 0) {
DownloadItemInfo sameDown = infoList.get(0);
if (sameDown.getCurrentLen() == sameDown.getTotalLen()) {
synchronized (applisteners) {
for (IDownloadCallable downloadCallable : applisteners) {
downloadCallable.onDownloadError(sameDown.getId(), 2, "文件已经下载了");
}
}
}
}
//插入数据库
int recrodId = downloadDao.addRecrod(url, filePath, displayName, priority.getValue());
if (recrodId != -1) {
synchronized (applisteners) {
for (IDownloadCallable downloadCallable : applisteners) {
//通知应用层 数据库被添加了
downloadCallable.onDownloadInfoAdd(downloadItemInfo.getId());
}
}
}
//插入失败时,再次进行查找,确保能查得到
else {
//插入
downloadItemInfo = downloadDao.findRecord(url, filePath);
}
}
//是否正在下载
if (isDowning(file.getAbsolutePath())) {
synchronized (applisteners) {
for (IDownloadCallable downloadCallable : applisteners) {
downloadCallable.onDownloadError(downloadItemInfo.getId(), 4, "正在下载,请不要重复添加");
}
}
return downloadItemInfo.getId();
}

if (downloadItemInfo != null) {
downloadItemInfo.setPriority(priority.getValue());
downloadItemInfo.setStopMode(DownloadStopMode.auto.getValue());
//判断数据库存的 状态是否是完成
if (downloadItemInfo.getStatus() != DownloadStatus.finish.getValue()) {
if (downloadItemInfo.getTotalLen() == 0L || file.length() == 0L) {
Log.i(TAG, "还未开始下载");
downloadItemInfo.setStatus(DownloadStatus.failed.getValue());
}
//判断数据库中 总长度是否等于文件长度
if (downloadItemInfo.getTotalLen() == file.length() && downloadItemInfo.getTotalLen() != 0) {
downloadItemInfo.setStatus(DownloadStatus.finish.getValue());
synchronized (applisteners) {
for (IDownloadCallable downloadCallable : applisteners) {
try {
downloadCallable.onDownloadError(downloadItemInfo.getId(), 4, "已经下载了");
} catch (Exception e) {
}
}
}
}
} else {
if (!file.exists() || (downloadItemInfo.getTotalLen() != downloadItemInfo.getCurrentLen())) {
downloadItemInfo.setStatus(DownloadStatus.failed.getValue());
}
}
//更新
downloadDao.updateRecord(downloadItemInfo);

//判断是否已经下载完成
if (downloadItemInfo.getStatus() == DownloadStatus.finish.getValue()) {
Log.i(TAG, "已经下载完成 回调应用层");
final int downId = downloadItemInfo.getId();
synchronized (applisteners) {
handler.post(new Runnable() {
@Override
public void run() {
for (IDownloadCallable downloadCallable : applisteners) {
downloadCallable.onDownloadStatusChanged(downId, DownloadStatus.finish);
}
}
});
}
downloadDao.removeRecordFromMemery(downId);
return downloadItemInfo.getId();
}//之前的下载 状态为暂停状态
List<DownloadItemInfo> allDowning = downloadFileTaskList;
//当前下载不是最高级 则先退出下载
if (priority != Priority.high) {
for (DownloadItemInfo downling : allDowning) {
//从下载表中,获取到全部正在下载的任务
downling = downloadDao.findSigleRecord(downling.getFilePath());
if (downling != null && downling.getPriority() == Priority.high.getValue()) {
if (downling.getFilePath().equals(downloadItemInfo.getFilePath())) {
break;
} else {
return downloadItemInfo.getId();
}
}
}
}
//
reallyDown(downloadItemInfo);
if (priority == Priority.high || priority == Priority.middle) {
synchronized (allDowning) {
for (DownloadItemInfo downloadItemInfo1 : allDowning) {
if (!downloadItemInfo.getFilePath().equals(downloadItemInfo1.getFilePath())) {
DownloadItemInfo downingInfo = downloadDao.findSigleRecord(downloadItemInfo1.getFilePath());
if (downingInfo != null) {
pause(downloadItemInfo.getId(), DownloadStopMode.auto);
}
}
}
}
return downloadItemInfo.getId();
}
}
return -1;
}

//停止
public void pause(int downloadId, DownloadStopMode mode) {
if (mode == null) {
mode = DownloadStopMode.auto;
}
final DownloadItemInfo downloadInfo = downloadDao.findRecordById(downloadId);
if (downloadInfo != null) {
// 更新停止状态
if (downloadInfo != null) {
downloadInfo.setStopMode(mode.getValue());
downloadInfo.setStatus(DownloadStatus.pause.getValue());
downloadDao.updateRecord(downloadInfo);
}
for (DownloadItemInfo downing : downloadFileTaskList) {
if (downloadId == downing.getId()) {
downing.getHttpTask().pause();
}
}
}
}

//判断当前是否正在下载
private boolean isDowning(String absolutePath) {
for (DownloadItemInfo downloadItemInfo : downloadFileTaskList) {
if (downloadItemInfo.getFilePath().equals(absolutePath)) {
return true;
}
}
return false;
}

//添加观察者
public void setDownCallable(IDownloadCallable downloadCallable) {
synchronized (applisteners) {
applisteners.add(downloadCallable);
}
}

//下载
public DownloadItemInfo reallyDown(DownloadItemInfo downloadItemInfo) {
synchronized (lock) {
//实例化DownloadItem
RequestHolder requestHolder = new RequestHolder();
//设置请求下载的策略
IHttpService httpService = new FileDownHttpService();
//得到请求头的参数 map
Map<String, String> map = httpService.getHttpHeadMap();
//处理结果的策略
IHttpListener httpListener = new DownLoadListener(downloadItemInfo, this, httpService);
requestHolder.setHttpListener(httpListener);
requestHolder.setHttpService(httpService);
requestHolder.setUrl(downloadItemInfo.getUrl());
HttpTask httpTask = new HttpTask(requestHolder);
downloadItemInfo.setHttpTask(httpTask);
//添加
downloadFileTaskList.add(downloadItemInfo);
httpTask.start();
}
return downloadItemInfo;
}

@Override
public void onDownloadStatusChanged(DownloadItemInfo downloadItemInfo) {
Log.d(TAG, "onDownloadStatusChanged");
}

@Override
public void onTotalLengthReceived(DownloadItemInfo downloadItemInfo) {
Log.d(TAG, "onTotalLengthReceived");
}

@Override
public void onCurrentSizeChanged(DownloadItemInfo downloadItemInfo, double downLength, long speed) {
Log.i(TAG, "下载速度: " + speed / 1000 + "k/s");
Log.i(TAG, "路径: " + downloadItemInfo.getFilePath() + ",下载进度: " + downLength + ",速度: " + speed);
}

@Override
public void onDownloadSuccess(DownloadItemInfo downloadItemInfo) {
Log.i(TAG, "下载成功! 路径: " + downloadItemInfo.getFilePath() + ",url: " + downloadItemInfo.getUrl());
}

@Override
public void onDownloadPause(DownloadItemInfo downloadItemInfo) {
Log.d(TAG, "onDownloadPause");
}

@Override
public void onDownloadError(DownloadItemInfo downloadItemInfo, int var2, String var3) {
Log.d(TAG, "onDownloadError " + var3);
}
}

完善DownloadItemInfo类

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
@DbTable("tb_downloadInfo")
public class DownloadItemInfo extends BaseEntity<DownloadItemInfo> {
public long currentLength;
public long totalLength;
public Integer id;
public Integer priority;
public Integer stopMode;
public Integer status;
public Long totalLen;
public Long currentLen;
public String displayName;
public String startTime;
public String finishTime;
public String userId;
public String httpTaskType;
public String url;
public String filePath;
private transient HttpTask httpTask;
// private DownloadStatus status;

public DownloadItemInfo() {
}

public DownloadItemInfo(String url, String filePath) {
this.url = url;
this.filePath = filePath;
}

public long getCurrentLength() {
return currentLength;
}

public void setCurrentLength(long currentLength) {
this.currentLength = currentLength;
}

public long getTotalLength() {
return totalLength;
}

public void setTotalLength(long totalLength) {
this.totalLength = totalLength;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public Integer getPriority() {
return priority;
}

public void setPriority(Integer priority) {
this.priority = priority;
}

public Integer getStopMode() {
return stopMode;
}

public void setStopMode(Integer stopMode) {
this.stopMode = stopMode;
}

public Integer getStatus() {
return status;
}

public void setStatus(Integer status) {
this.status = status;
}

public Long getTotalLen() {
return totalLen;
}

public void setTotalLen(Long totalLen) {
this.totalLen = totalLen;
}

public Long getCurrentLen() {
return currentLen;
}

public void setCurrentLen(Long currentLen) {
this.currentLen = currentLen;
}

public String getDisplayName() {
return displayName;
}

public void setDisplayName(String displayName) {
this.displayName = displayName;
}

public String getStartTime() {
return startTime;
}

public void setStartTime(String startTime) {
this.startTime = startTime;
}

public String getFinishTime() {
return finishTime;
}

public void setFinishTime(String finishTime) {
this.finishTime = finishTime;
}

public String getUserId() {
return userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

public String getHttpTaskType() {
return httpTaskType;
}

public void setHttpTaskType(String httpTaskType) {
this.httpTaskType = httpTaskType;
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

public String getFilePath() {
return filePath;
}

public void setFilePath(String filePath) {
this.filePath = filePath;
}

public HttpTask getHttpTask() {
return httpTask;
}

public void setHttpTask(HttpTask httpTask) {
this.httpTask = httpTask;
}
}

Donate comment here