移动架构33_网络访问框架与数据库框架实现断点下载
2017-11-12 11:19
525 查看
Demo地址:https://gitee.com/YuBaoZi/AndroidWenJianDuanDianXiaZai/blob/master/Download.rar
断点下载;
下载任务有优先级;
下载有回调;
网络自封装(fastjson和HttpClient)
数据库存储下载信息;
拓展性好,可以针对不同用户;
要点:
掌握网络框架的搭建
掌握数据库框架的封装
掌握数据的下载
网络框架和数据库框架的联合封装使用
数据库作用:记录文件下载记录、文件总长度、文件是否已经下载,可以不用实时更改当前文件的下载进度,利用File的长度,作为下载文件的起始值
断点下载;
下载任务有优先级;
下载有回调;
网络自封装(fastjson和HttpClient)
数据库存储下载信息;
拓展性好,可以针对不同用户;
要点:
掌握网络框架的搭建
掌握数据库框架的封装
掌握数据的下载
网络框架和数据库框架的联合封装使用
数据库作用:记录文件下载记录、文件总长度、文件是否已经下载,可以不用实时更改当前文件的下载进度,利用File的长度,作为下载文件的起始值
一、实现数据库底层框架
具体详解:移动架构29_面向对象式手写数据库架构设计一1、定义注解
实现业务类属性与数据库表中列名的兼容(翻译以及防止出现关键字)@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DbTable { String value(); }
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface DbFiled { String value(); }
2、定义操作数据的接口和抽象类
封装表创建、增删改查的行为;业务生成sql语句、查询结果生成业务结果的逻辑是一致的,将其封装。public interface IBaseDao<T> { /** * 插入数据 * @param entity * @return */ Long insert(T entity); /** * * @param entity * @param where * @return */ int update(T entity, T where); /** * 删除数据 * @param where * @return */ int delete(T where); /** * 查询数据 */ List<T> query(T where); List<T> query(T where, String orderBy, Integer startIndex, Integer limit); List<T> query(String sql); }
public abstract class BaseDao<T> implements IBaseDao<T> { private boolean isInit = false; protected SQLiteDatabase sqLiteDatabase; private Class<T> entityClass; private String tableName; private Map<String, Field> cacheMap; public String getTableName() { return tableName; } @Override public Long insert(T entity) { ContentValues contentValues = getContentValues(entity); long result = sqLiteDatabase.insert(tableName, null, contentValues); return result; } private ContentValues getContentValues(T entity) { ContentValues contentValues = new ContentValues(); try { for (Map.Entry<String,Field> me:cacheMap.entrySet()){ if(me.getValue().get(entity)==null){ continue; } contentValues.put(me.getKey(),me.getValue().get(entity).toString()); } } catch (IllegalAccessException e) { e.printStackTrace(); } return contentValues; } @Override public int update(T entity, T where) { ContentValues contentValues = getContentValues(entity); Condition condition = new Condition(getContentValues(where)); int update = sqLiteDatabase.update(tableName, contentValues, condition.whereClause, condition.whereArgs); return update; } @Override public int delete(T where) { Condition condition = new Condition(getContentValues(where)); int delete = sqLiteDatabase.delete(tableName, condition.whereClause, condition.whereArgs); return delete; } @Override public List<T> query(T where) { return query(where,null,null,null); } @Override public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) { String limitString = null; if(startIndex!=null && limit!=null){ limitString = startIndex+","+limit; } Condition condition = new Condition(getContentValues(where)); Cursor cursor = null; List<T> result =new ArrayList<>(); try { cursor = sqLiteDatabase.query(tableName, null,condition.getWhereClause(),condition.whereArgs,null,null,orderBy,limitString); result=getResult(cursor,where); }catch (Exception e){ e.printStackTrace(); }finally { if(cursor!=null){ cursor.close(); } } return result; } protected List<T> getResult(Cursor cursor, T where) { ArrayList list = new ArrayList(); Object item; while(cursor.moveToNext()){ try { item = where.getClass().newInstance(); Iterator<Map.Entry<String, Field>> iterator = cacheMap.entrySet().iterator(); while(iterator.hasNext()){ Map.Entry<String, Field> entry = iterator.next(); String colmunName = entry.getKey(); Field field = entry.getValue(); int columnIndex = cursor.getColumnIndex(colmunName); Class type = field.getType(); if(columnIndex!=-1){ if(type==String.class){ field.set(item,cursor.getString(columnIndex)); }else if(type==Double.class){ field.set(item,cursor.getDouble(columnIndex)); }else if(type== Integer.class){ field.set(item,cursor.getInt(columnIndex)); }else if(type == Long.class){ field.set(item,cursor.getLong(columnIndex)); }else if(type == byte[].class){ field.set(item,cursor.getBlob(columnIndex)); }else{ /** * 不支持的类型 */ continue; } } } list.add(item); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } return list; } protected synchronized boolean init(Class<T> entity, SQLiteDatabase sqLiteDatabase) { if (!isInit) { this.sqLiteDatabase = sqLiteDatabase; this.entityClass = entity; if (entity.getAnnotation(DbTable.class) == null) { tableName = entity.getClass().getSimpleName(); } else { tableName = entity.getAnnotation(DbTable.class).value(); } if (!sqLiteDatabase.isOpen()) { return false; } if (!TextUtils.isEmpty(createTable())) { sqLiteDatabase.execSQL(createTable()); } cacheMap = new HashMap<>(); initCatchMap(); isInit = true; } return isInit; } /** * 维护映射关系 */ private void initCatchMap() { String sql = "select * from " + this.tableName + " limit 1,0"; Cursor cursor = null; try { cursor = sqLiteDatabase.rawQuery(sql, null); /** * 表的列名数组 */ String[] columnNames = cursor.getColumnNames(); /** * 拿到Filed数组 */ Field[] columnFields = entityClass.getFields(); for (Field filed : columnFields) { filed.setAccessible(true); Field columnFiled = null; String colmunName = null; /** * 开始找对应关系 */ for (String cn : columnNames) { String filedName = null; if(filed.getAnnotation(DbFiled.class)!=null){ filedName = filed.getAnnotation(DbFiled.class).value(); }else { filedName = filed.getName(); } if(cn.equals(filedName)){ columnFiled = filed; colmunName = cn; break; } } //找到了对应关系 if(columnFiled!=null){ cacheMap.put(colmunName,columnFiled); } } } catch (Exception e) { e.printStackTrace(); } finally { cursor.close(); } } public abstract String createTable(); class Condition{ /** * 查询条件 * name=? && password =? */ private String whereClause; private String[] whereArgs; public Condition(ContentValues whereClause) { ArrayList list=new ArrayList(); StringBuilder stringBuilder=new StringBuilder(); stringBuilder.append(" 1=1 "); Set keys=whereClause.keySet(); Iterator iterator=keys.iterator(); while (iterator.hasNext()) { String key= (String) iterator.next(); String value= (String) whereClause.get(key); if (value!=null) { /* 拼接条件查询语句 1=1 and name =? and password=? */ stringBuilder.append(" and "+key+" =?"); /** * ?----》value */ list.add(value); } } this.whereClause=stringBuilder.toString(); this.whereArgs= (String[]) list.toArray(new String[list.size()]); } public String[] getWhereArgs() { return whereArgs; } public String getWhereClause() { return whereClause; } } }
3、定义提供操作数据库类的工厂类
校验数据库创建、根据类型提供操作表的操作类public class BaseDaoFactory { private String sqlitDatabasePath; private SQLiteDatabase sqLiteDatabase; private static BaseDaoFactory instance = new BaseDaoFactory(); private BaseDaoFactory() { sqlitDatabasePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/teacher.db"; openDatabase(); } public synchronized <T extends BaseDao<M>, M> T getDataHelper(Class<T> clazz, Class<M> entityClass) { T baseDao = null; try { baseDao = clazz.newInstance(); baseDao.init(entityClass, sqLiteDatabase); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return baseDao; } private void openDatabase() { sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqlitDatabasePath, null); } public static BaseDaoFactory getInstance() { return instance; } }
二、定义网络框架
具体详解:移动架构32_自定义网络访问框架开发1、定义网络请求的接口
public interface IHttpListener { /** * 网络访问 * 处理结果 回调 * @param httpEntity */ void onSuccess(HttpEntity httpEntity); void onFail(); void addHttpHeader(Map<String,String> headerMap); }
public interface IHttpService { /** * 设置url * @param url */ void setUrl(String url); /** * 执行获取网络 */ void excute(); /** * 设置处理接口 * @param httpListener */ void setHttpListener(IHttpListener httpListener); /** * 设置请求参数 * String 1 * byte[] 2 * */ void setRequestData(byte[] requestData); void pause(); /** * * 以下的方法是 额外添加的 * 获取请求头的map * @return */ Map<String,String> getHttpHeadMap(); boolean cancle(); boolean isCancle(); boolean isPause(); }
2、请求与回调封装到一个类中
public class RequestHodler<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; } }
3、定义管理网络请求的线程池
public class ThreadPoolManager { private static final String TAG ="ThreadPoolManager" ; private static ThreadPoolManager instance=new ThreadPoolManager(); private LinkedBlockingQueue<Future<?>> taskQuene=new LinkedBlockingQueue<>(); private ThreadPoolExecutor threadPoolExecutor; public static ThreadPoolManager getInstance() { return instance; } private ThreadPoolManager() { threadPoolExecutor=new ThreadPoolExecutor(4,10,10, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(4), handler); threadPoolExecutor.execute(runable); } public <T> boolean removeTask(FutureTask futureTask) { boolean result=false; /** * 阻塞式队列是否含有线程 */ if(taskQuene.contains(futureTask)) { taskQuene.remove(futureTask); }else { result=threadPoolExecutor.remove(futureTask); } return result; } private Runnable runable =new Runnable() { @Override public void run() { while (true) { FutureTask futrueTask=null; try { /** * 阻塞式函数 */ Log.i(TAG,"等待队列 "+taskQuene.size()); futrueTask= (FutureTask) taskQuene.take(); } catch (InterruptedException e) { e.printStackTrace(); } if(futrueTask!=null) { threadPoolExecutor.execute(futrueTask); } Log.i(TAG,"线程池大小 "+threadPoolExecutor.getPoolSize()); } } }; public <T> void execte(FutureTask<T> futureTask) throws InterruptedException { taskQuene.put(futureTask); } private RejectedExecutionHandler handler=new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { try { taskQuene.put(new FutureTask<Object>(r,null) { }); } catch (InterruptedException e) { e.printStackTrace(); } } }; }
4、定义异步操作作为网络请求实现的平台
public class HttpTask<T> implements Runnable { private IHttpService httpService; private FutureTask futureTask; public HttpTask(RequestHodler<T> requestHodler) { httpService=requestHodler.getHttpService(); httpService.setHttpListener(requestHodler.getHttpListener()); httpService.setUrl(requestHodler.getUrl()); //增加方法 IHttpListener httpListener=requestHodler.getHttpListener(); httpListener.addHttpHeader(httpService.getHttpHeadMap()); try { T request=requestHodler.getRequestInfo(); if(request!=null) { String requestInfo= JSON.toJSONString(request); httpService.setRequestData(requestInfo.getBytes("UTF-8")); } } catch (Exception e) { e.printStackTrace(); } } @Override public void run() { httpService.excute(); } /** * 新增方法 */ public void start() { futureTask=new FutureTask(this,null); try { ThreadPoolManager.getInstance().execte(futureTask); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 新增方法 */ public void pause() { httpService.pause(); if(futureTask!=null) { ThreadPoolManager.getInstance().removeTask(futureTask); } } }
三、在网络与数据库框架的基础上再次封装
1、定义下载状态、优先级、停止模式
public enum DownloadStatus { waitting(0), starting(1), downloading(2), pause(3), finish(4), failed(5) ; private int value; private DownloadStatus(int value) { this.value=value; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } }
public enum Priority { /** * 手动下载的优先级 */ low(0), /** * 主动推送资源的手动恢复的优先级 */ middle(1), /** * 主动推送资源的优先级 */ high(2); Priority(int value) { this.value = value; } private int value; public int getValue() { return value; } public void setValue(int value) { this.value = 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); DownloadStopMode(Integer value) { this.value = value; } /** * 值 */ private Integer value; public Integer getValue() { return value; } public void setValue(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; } }
2、定义业务Bean及其基类
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; } }
@DbTable("t_downloadInfo") public class DownloadItemInfo extends BaseEntity<DownloadItemInfo> { public long currentLength; public long totalLength; public transient HttpTask httpTask; /** * 下载id */ public Integer id; /** * 下载url */ public String url; /** * 下载存储的文件路径 */ public String filePath; /** * 下载文件显示名 */ public String displayName; /** * 下载文件总大小 */ public Long totalLen; /** * 下载文件当前大小 */ public Long currentLen; /** * 下载开始时间 */ public String startTime; /** * 下载结束时间 */ public String finishTime; /** * 用户id */ public String userId; /** * 下载任务类型 */ public String httpTaskType; /** * 下载优先级 */ public Integer priority; /** * 下载停止模式 */ public Integer stopMode; //下载的状态 public Integer status; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } 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 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 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 DownloadItemInfo(String url, String filePath) { this.url = url; this.filePath = filePath; } public DownloadItemInfo( ) { } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } 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 void setStartTime(String startTime) { this.startTime = startTime; } }
3、定义下载的接口
public interface IDownLitener extends IHttpListener{ void setHttpServive(IHttpService httpServive); void setCancleCalle(); void setPuaseCallble(); }
/** * 断点续传监听接口 */ public interface IDownloadCallable { /** * 新增下载任务的监听 * * @param downloadId * 下载id */ void onDownloadInfoAdd(int downloadId); /** * 删除下载任务的监听 * * @param downloadId * 下载id */ void onDownloadInfoRemove(int downloadId); /** * 下载状态变化 * * @param downloadId * 下载id * @param status * 下载状态 */ void onDownloadStatusChanged(int downloadId, DownloadStatus status); /** * 获取了下载文件总的长度 * * @param downloadId * 下载id * @param totalLength * 下载文件总的长度 */ void onTotalLengthReceived(int downloadId, long totalLength); /** * 下载进度 * * @param downloadId * 下载id * @param downloadpercent * 下载的百分比 * @param speed * 下载速度 */ void onCurrentSizeChanged(int downloadId, double downloadpercent, long speed); /** * 下载成功 * * @param downloadId * 下载id */ void onDownloadSuccess(int downloadId); /** * 下载失败监听 * * @param downloadId * 下载id * @param errorCode * 下载错误码 * @param errorMsg * 下载错误信息 */ void onDownloadError(int downloadId, int errorCode, String errorMsg); }
public interface IDownloadServiceCallable { void onDownloadStatusChanged(DownloadItemInfo downloadItemInfo); void onTotalLengthReceived(DownloadItemInfo downloadItemInfo); void onCurrentSizeChanged(DownloadItemInfo downloadItemInfo, double downLenth, long speed); void onDownloadSuccess(DownloadItemInfo downloadItemInfo); void onDownloadPause(DownloadItemInfo downloadItemInfo); void onDownloadError(DownloadItemInfo downloadItemInfo, int var2, String var3); }
4、实现数据具体操作
public class DownLoadDao extends BaseDao<DownloadItemInfo> { /** * 保存应该下载的集合 * 不包括已经下载成功的 */ private List<DownloadItemInfo> downloadItemInfoList= Collections.synchronizedList(new ArrayList<DownloadItemInfo>()); private DownloadInfoComparator downloadInfoComparator=new DownloadInfoComparator(); @Override public String createTable() { return "create table if not exists t_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))"; } @Override public List<DownloadItemInfo> query(String sql) { return null; } /** * id */ /** * 生成下载id * * @return 返回下载id */ private Integer generateRecordId() { int maxId = 0; String sql = "select max(id) from " +getTableName() ; synchronized (DownLoadDao.class) { Cursor cursor = this.sqLiteDatabase.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; } /** * 根据下载地址和下载文件路径查找下载记录 * * @param url * 下载地址 * @param filePath * 下载文件路径 * @return */ 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; } } /** * 根据 下载文件路径查找下载记录 * * 下载地址 * @param filePath * 下载文件路径 * @return */ public List<DownloadItemInfo> findRecord(String filePath) { synchronized (DownLoadDao.class) { DownloadItemInfo where = new DownloadItemInfo(); where.setFilePath(filePath); List<DownloadItemInfo> resultList = super.query(where); return resultList; } } /** * 添加下载记录 * * @param url * 下载地址 * @param filePath * 下载文件路径 * @param displayName * 文件显示名 * @param priority * 小组优先级 * TODO * @return 下载id */ 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); java.text.SimpleDateFormat dateFormat = new java.text.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; } } /** * 更新下载记录 * * @param record * 下载记录 * @return */ 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; } /** * 根据下载地址和下载文件路径查找下载记录 * * 下载地址 * @param filePath * 下载文件路径 * @return */ public DownloadItemInfo findSigleRecord(String filePath) { List<DownloadItemInfo> downloadInfoList = findRecord(filePath); if(downloadInfoList.isEmpty()) { return null; } return downloadInfoList.get(0); } /** * 根据id查找下载记录对象 * * @param recordId * @return */ 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从内存中移除下载记录 * * @param id * 下载id * @return true标示删除成功,否则false */ 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 lhs, DownloadItemInfo rhs) { return rhs.getId() - lhs.getId(); } } }
5、实现下载和回调的接口
public class DownLoadLitener implements IDownLitener{ private DownloadItemInfo downloadItemInfo; private File file; protected String url; private long breakPoint; private IDownloadServiceCallable downloadServiceCallable; private IHttpService httpService; /** * 得到主线程 */ private Handler handler=new Handler(Looper.getMainLooper()); public DownLoadLitener(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(); } /** * 2 * @param headerMap */ public void addHttpHeader(Map<String,String> headerMap) { long length=getFile().length(); if(length>0L) { headerMap.put("RANGE","bytes="+length+"-"); } } public DownLoadLitener(DownloadItemInfo downloadItemInfo) { this.downloadItemInfo = downloadItemInfo; } @Override public void setHttpServive(IHttpService httpServive) { this.httpService=httpServive; } /** * 设置取消接口 */ @Override public void setCancleCalle() { } @Override public void setPuaseCallble() { } @Override public void onSuccess(HttpEntity httpEntity) { InputStream inputStream = null; try { inputStream = httpEntity.getContent(); } catch (IOException e) { e.printStackTrace(); } long startTime = System.currentTimeMillis(); //用于计算每秒多少k long speed = 0L; //花费时间 long useTime = 0L; //下载的长度 long getLen = 0L; //接受的长度 long receiveLen = 0L; boolean bufferLen = false; //得到下载的长度 long dataLength = httpEntity.getContentLength(); //单位时间下载的字节数 long calcSpeedLen = 0L; //总数 long totalLength = this.breakPoint + dataLength; //更新数量 this.receviceTotalLength(totalLength); //更新状态 this.downloadStatusChange(DownloadStatus.downloading); byte[] buffer = new byte[512]; int count = 0; long currentTime = System.currentTimeMillis(); BufferedOutputStream bos = null; FileOutputStream fos = null; try { if (!makeDir(this.getFile().getParentFile())) { downloadServiceCallable.onDownloadError(downloadItemInfo,1,"创建文件夹失败"); } else { fos = new FileOutputStream(this.getFile(), true); bos = new BufferedOutputStream(fos); int length = 1; while ((length = inputStream.read(buffer)) != -1) { if (this.getHttpService().isCancle()) { downloadServiceCallable.onDownloadError(downloadItemInfo, 1, "用户取消了"); return; } if (this.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; //应该保存数据库 this.downloadLengthChange(this.breakPoint + getLen, totalLength, speed); } } bos.close(); inputStream.close(); if (dataLength != getLen) { downloadServiceCallable.onDownloadError(downloadItemInfo, 3, "下载长度不相等"); } else { this.downloadLengthChange(this.breakPoint + getLen, totalLength, speed); this.downloadServiceCallable.onDownloadSuccess(downloadItemInfo.copy()); } } } catch (IOException ioException) { if (this.getHttpService() != null) { // this.getHttpService().abortRequest(); } return; } catch (Exception e) { if (this.getHttpService() != null) { // this.getHttpService().abortRequest(); } } finally { try { if (bos != null) { bos.close(); } if (httpEntity != null) { inputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } } /** * 创建文件夹的操作 * @param parentFile * @return */ private boolean makeDir(File parentFile) { return parentFile.exists()&&!parentFile.isFile() ?parentFile.exists()&&parentFile.isDirectory(): parentFile.mkdirs(); } private void downloadLengthChange(final long downlength, final long totalLength, final long speed) { downloadItemInfo.setCurrentLength(downlength); if(downloadServiceCallable!=null) { DownloadItemInfo copyDownItenIfo=downloadItemInfo.copy(); synchronized (this.downloadServiceCallable) { handler.post(new Runnable() { @Override public void run() { downloadServiceCallable.onCurrentSizeChanged(downloadItemInfo,(double) downlength/(double)totalLength,speed); } }); } } } /** * 更改下载时的状态 * @param downloading */ private void downloadStatusChange(DownloadStatus downloading) { downloadItemInfo.setStatus(downloading.getValue()); final DownloadItemInfo copyDownloadItemInfo=downloadItemInfo.copy(); if(downloadServiceCallable!=null) { synchronized (this.downloadServiceCallable) { handler.post(new Runnable() { @Override public void run() { downloadServiceCallable.onDownloadStatusChanged(copyDownloadItemInfo); } }); } } } /** * 回调 长度的变化 * @param totalLength */ private void receviceTotalLength(long totalLength) { downloadItemInfo.setCurrentLength(totalLength); final DownloadItemInfo copyDownloadItemInfo=downloadItemInfo.copy(); if(downloadServiceCallable!=null) { synchronized (this.downloadServiceCallable) { handler.post(new Runnable() { @Override public void run() { downloadServiceCallable.onTotalLengthReceived(copyDownloadItemInfo); } }); } } } @Override public void onFail() { } public IHttpService getHttpService() { return httpService; } public File getFile() { return file; } }
public class FileDownHttpService implements IHttpService { private static final String TAG = "FileDownHttpService"; /** * 即将添加到请求头的信息 */ private Map<String ,String> headerMap= Collections.synchronizedMap(new HashMap<String ,String>()); /** * 含有请求处理的 接口 */ private IHttpListener httpListener; private HttpClient httpClient=new DefaultHttpClient(); private HttpGet httpPost; private String url; private byte[] requestDate; /** * httpClient获取网络的回调 */ private HttpRespnceHandler httpRespnceHandler=new HttpRespnceHandler(); /** * 增加方法 */ private AtomicBoolean pause=new AtomicBoolean(false); @Override public void setUrl(String url) { this.url=url; } @Override public void excute() { httpPost=new HttpGet(url); constrcutHeader(); // ByteArrayEntity byteArrayEntity=new ByteArrayEntity(requestDate); // httpPost.setEntity(byteArrayEntity); try { httpClient.execute(httpPost,httpRespnceHandler); } catch (IOException e) { httpListener.onFail(); } } /** * 1 */ private void constrcutHeader() { Iterator iterator=headerMap.keySet().iterator(); while (iterator.hasNext()) { String key= (String) iterator.next(); String value=headerMap.get(key); Log.i(TAG," 请求头信息 "+key+" value "+value); httpPost.addHeader(key,value); } } public Map<String, String> getHeaderMap() { return headerMap; } @Override public void setHttpListener(IHttpListener httpListener) { this.httpListener=httpListener; } @Override public void setRequestData(byte[] requestData) { this.requestDate=requestData; } @Override public void pause() { pause.compareAndSet(false,true); } @Override public Map<String, String> getHttpHeadMap() { return headerMap; } @Override public boolean cancle() { return false; } @Override public boolean isCancle() { return false; } @Override public boolean isPause() { return pause.get(); } private class HttpRespnceHandler extends BasicResponseHandler { @Override public String handleResponse(HttpResponse response) throws ClientProtocolException { //响应吗 int code=response.getStatusLine().getStatusCode(); if(code==200||206==code) { httpListener.onSuccess(response.getEntity()); }else { ListView list; httpListener.onFail(); } return null; } } }
6、将整个逻辑进行封装、链接、启动,形成调用工具类
public class DownFileManager implements IDownloadServiceCallable { private static final String TAG ="DownFileManager " ; // private static private byte[] lock=new byte[0]; DownLoadDao downLoadDao= BaseDaoFactory.getInstance(). getDataHelper(DownLoadDao.class,DownloadItemInfo.class); java.text.SimpleDateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); /** * 观察者模式 */ private final List<IDownloadCallable> applisteners = new CopyOnWriteArrayList<IDownloadCallable>(); /** * 正在下载的所有任务 */ private static List<DownloadItemInfo> downloadFileTaskList = new CopyOnWriteArrayList(); Handler handler=new Handler(Looper.getMainLooper()); public int download(String url) { String[] preFix=url.split("/"); return this.download(url,Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+preFix[preFix.length-1]); } public int download(String url, String filePath ) { String[] preFix=url.split("/"); String displayName=preFix[preFix.length-1]; return this.download(url,filePath,displayName); } public int download(String url, String filePath, String displayName) { return this.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> samesFile=downLoadDao.findRecord(filePath); /** * 大于0 表示下载 */ if(samesFile.size()>0) { DownloadItemInfo sameDown=samesFile.get(0); if(sameDown.getCurrentLen()==sameDown.getTotalLen()) { synchronized (applisteners) { for (IDownloadCallable downloadCallable:applisteners) { downloadCallable.onDownloadError(sameDown.getId(),2,"文件已经下载了"); } } } } /**--------------------------------------------- * 插入数据库 * 可能插入失败 * 因为filePath 和id是独一无二的 在数据库建表时已经确定了 */ 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()) { /** * 更改--------- * 当前下载级别不是最高级 传进来的是middle 但是在数据库中查到路径一模一样 的记录 所以他也是最高级------------------------------ * 比如 第一次下载是用最高级下载,app闪退后,没有下载完成,第二次传的是默认级别,这样就应该是最高级别下载 */ if (downling.getFilePath().equals(downloadItemInfo.getFilePath())) { break; } else { return downloadItemInfo.getId(); } // if(downloadItemInfo.getFilePath().equals(downling.getFilePath())) // { // 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; } /** * 停止 * @param downloadId * @param mode */ 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(); } } } } /** * 判断当前是否正在下载 * * @param absolutePath * @return */ private boolean isDowning(String absolutePath) { for (DownloadItemInfo downloadItemInfo:downloadFileTaskList) { if(downloadItemInfo.getFilePath().equals(absolutePath)) { return true; } } return false; } /** * 添加观察者 * @param downloadCallable */ public void setDownCallable(IDownloadCallable downloadCallable) { synchronized (applisteners) { applisteners.add(downloadCallable); } } /** * 下载 */ public DownloadItemInfo reallyDown(DownloadItemInfo downloadItemInfo) { synchronized (lock) { //实例化DownloadItem RequestHodler requestHodler=new RequestHodler(); //设置请求下载的策略 IHttpService httpService=new FileDownHttpService(); //得到请求头的参数 map Map<String,String> map=httpService.getHttpHeadMap(); /** * 处理结果的策略 */ IHttpListener httpListener=new DownLoadLitener(downloadItemInfo,this,httpService); requestHodler.setHttpListener(httpListener); requestHodler.setHttpService(httpService); /** * bug url */ requestHodler.setUrl(downloadItemInfo.getUrl()); HttpTask httpTask=new HttpTask(requestHodler); downloadItemInfo.setHttpTask(httpTask); /** * 添加 */ downloadFileTaskList.add(downloadItemInfo); httpTask.start(); } return downloadItemInfo; } @Override public void onDownloadStatusChanged(DownloadItemInfo downloadItemInfo) { } @Override public void onTotalLengthReceived(DownloadItemInfo downloadItemInfo) { } @Override public void onCurrentSizeChanged(DownloadItemInfo downloadItemInfo, double downLenth, long speed) { Log.i(TAG,"下载速度:"+ speed/1000 +"k/s"); Log.i(TAG,"-----路径 "+ downloadItemInfo.getFilePath()+" 下载长度 "+downLenth+" 速度 "+speed); } @Override public void onDownloadSuccess(DownloadItemInfo downloadItemInfo) { Log.i(TAG,"下载成功 路劲 "+ downloadItemInfo.getFilePath()+" url "+ downloadItemInfo.getUrl()); } @Override public void onDownloadPause(DownloadItemInfo downloadItemInfo) { } @Override public void onDownloadError(DownloadItemInfo downloadItemInfo, int var2, String var3) { } }
四、调用
DownFileManager downFileService=new DownFileManager(); downFileService.download("http://gdown.baidu.com/data/wisegame/8be18d2c0dc8a9c9/WPSOffice_177.apk");
相关文章推荐
- 移动架构32_自定义网络访问框架开发
- 33、多线程断点下载的实现&界面的更新
- Android网络优化5-实现高效网络访问来优化下载
- 自己写的网络数据请求 第三方框架 断点续传 上传下载
- iOS开发之网络编程--3、NSURLSessionDataTask实现文件下载(离线断点续传下载)
- 移动网络游戏实现流程 -- 基于Cocos2d-x引擎和pomelo服务器框架
- Java网络编程之实现HTTP断点续传下载工具(附源代码)
- android网络编程之HttpUrlConnection的讲解--实现文件断点下载
- PDA移动POS终端系统,实现专柜或店铺的收货、零售、盘点通过无线网络直接连接总部中央数据库,实现高效安全的移动供应链管理
- 从此不求人:自主研发一套PHP前端开发框架(33)之插件化架构实现中
- [置顶] 基于java的网络爬虫框架(实现京东数据的爬取,并将插入数据库)
- 一步一步实现企业网络架构之九:利用Win2003 NAT功能配置Internet访问
- iOS开发之网络编程--4、NSURLSessionDataTask实现文件下载(离线断点续传下载) <进度值显示优化>
- iOS开发之网络编程--4、NSURLSessionDataTask实现文件下载(离线断点续传下载) <进度值显示优化>
- .NET三层架构的改进以及通用数据库访问组件的实现
- 安卓系统下的多线程断点下载实现2利用开源框架XUtils
- Python的Tornado框架实现异步非阻塞访问数据库的示例
- 基于c++和linux的线程池技术及框架实现(网络下载,测试并修改其中可能的错误)
- 从此不求人:自主研发一套PHP前端开发框架(33)之插件化架构实现下
- 【Android Developers Training】 83. 实现高效网络访问来优化下载