这里通过设计并实现一个网络框架的搭建,下载
设计思路
支持请求JSON文本类型,这样的话需要一个处理JSON的类,这里使用fastjson依赖
发出请求时,调用层不用关心上传参数,这里就需要对请求进行封装,并且将其封装的参数传递给框架,由框架去处理
获取数据后在框架内完成json数据的解析
回调时,调用层只需传入json的对应响应类,回调响应结果给主线程
并且需要支持高并发请求,使用线程池和人物列队
JSON数据访问解析实现步骤
首先定义三个接口,用于获取网络,处理数据和调用层回调
获取网络主要涉及到设置url,获取网络,监听接口,设置请求参数
//获取网络
public interface IHttpService {
//设置url
void setUrl(String url);
//执行获取网络
void excute();
//设置处理接口
void setHttpListener(IHttpListener httpListener);
//设置请求参数
void setRequestData(byte[] requestData);
}
处理数据会有成功和失败
//处理结果
public interface IHttpListener {
//处理结果 回调
void onSuccess(HttpEntity httpEntity);
void onFail();
}
回调与数据处理结果相统一
//回调
public interface IDataListener<M> {
//回调结果给调用层
void onSuccess(M m);
void onFail();
}
然后实现以上接口,完成网络的获取和数据的处理
完成网络的获取,在调用的时候完成相应的参数设置
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);
}
}
}
完成数据的处理,这里需要注意一点的是响应类的传入以及通知主线程的过程
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();
}
}
封装请求体,使调用更加方便
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;
}
}
之后就是设计线程池,用来执行任务,需要建立子线程
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里面使用的线程池
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;
}
}
最后建立调用层与框架层的联系关系
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数据的格式封装而成
测试网站的数据为:
{"code":0,"name":"jack","time":"2018-01-01","user_id":"10086"}
对应的Bean文件为
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中进行调用
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中声明
android {
···
useLibrary 'org.apache.http.legacy'
}
dependencies {
···
implementation 'com.alibaba:fastjson:1.+'
implementation 'httpcomponents-httpcore:httpcore:4.+'
}
这样就完成了json数据的访问和解析,接下来就是逐步完成其功能
完成下载功能
下载回调监听接口
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);
}
下载文件信息
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;
}
}
下载状态
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接口
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() {
}
}
在回调时侯使用克隆对象
//克隆对象,防止修改
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;
}
}
完成下载请求
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;
}
}
}
编写下载管理,实现下载封装
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,使其可以处理文件下载响应
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();
}
}
调用下载,完成下载测试
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
添加相应的下载操作数据库类
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();
}
}
}
策略枚举类
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;
}
}
停止模式枚举类
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
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类
@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;
}
}