您的位置:首页 > 理论基础 > 计算机网络

大文件 and 多下载任务的封装(三)--断点续传,下载类的整合封装,以及使用

2016-11-13 08:47 549 查看

ThreadManager

大文件 and 多下载任务的封装一

大文件 and 多下载任务的封装二

原文地址

上两篇文章中我们主要讲述了ThreadPool ,以及 Observer(观察者模式)这些基本的工具类只是起到了辅助的作用,现在我们来分析一下,直接从网络上;拉取数据的过程,

一.断电续传的操作原理\

首先我们假设读者都已经了解了基本的从网络上拉取数据文件的操作,那么现在我们只需要在原有的基础上增加一个方法,在HttpURLConnection类中提供了一个方法,

//设置范围,格式为Range:bytes x-y;
//也就是从currentPos 下载到 Maxsize,这个方法可以满足断点续传操作,我们要做的只是保存下载进度,然后调用这个方法即可
.setRequestProperty("Range", "bytes=" + currentPos + "-" + Maxsize);`


二.子线程的实现

根据第一篇文章我们可以了解到,如果想要使用线程池必须动态的添加与移除子线程,所以为了方便操作,我们可以新建一个类来执行下载的操作,伪代码如下:

//用来执行下载操作
class DownLoadTask implements Runnable{
@Override
public void run() {
// 1.设置范围,格式为Range:bytes x-y;
connection.setRequestProperty("Range", "bytes=" + dlInfo.currentPos + "-" + dlInfo.size);
randomAccessFile = new RandomAccessFile(file, "rwd");
//2.从当前已下载节点开始保存文件
randomAccessFile.seek(0 + dlInfo.currentPos);
//3.将要下载的文件写到保存在保存路径下的文件中
is = connection.getInputStream();
byte[] buffer = new byte[4096];
int length = -1;
while ((length = is.read(buffer)) != -1) {
randomAccessFile.write(buffer, 0, length);
currentPos += length;

}

}
}


三.使用

下面将给出ThreadManager,DownLoadManager 在一块完成文件下载的使用过程

DownLoadServer down = new DownLoadServer(){};
mDM = DownLoadManager.getInstance();
mDM.registerObserver(this);// 注册观察者, 监听状态和进度变化
mDM.download((T extends DownLoadInfo));


四.代码

DownLoadManager.java

//

package com.example.orchid.googleplatstore.manager;

import android.content.Intent;
import android.net.Uri;
import android.util.Log;

import com.example.orchid.googleplatstore.Utils.IOUtils;
import com.example.orchid.googleplatstore.Utils.UIUtils;
import com.example.orchid.googleplatstore.http.HttpHelper;
import com.example.orchid.googleplatstore.ui.domin.Appinfo;
import com.example.orchid.googleplatstore.ui.domin.DownLoadInfo;

import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

/**
* Created by orchid
* on 16-11-4.
* 使用观察者设计模式
* 1.创建被观察者模式的接口
* 2.创建被观察者集合,用来保存所有的观察者
* 3.注册观察者--将观察者接口,添加到集合中
* 4.注销观察者--将观察者接口从集合中移除
* 5.更新数据提醒观察者--循环遍历所有的观察者,状态,进度改变调用方法
*
*下载的状态分为六种,未下载--等待--下载中--暂停--下载错误--下载成功
*
*/

public class DownLoadManager {
private static final String TAG = "DownLoadManager";

public static final int STATE_UNDO = 1;
public static final int STATE_WAITING = 2;
public static final int STATE_DOWNLOADING = 3;
public static final int STATE_PAUSE = 4;
public static final int STATE_ERROR = 5;
public static final int STATE_SUCCESS = 6;

//单例模式,饿汉模式
private static DownLoadManager mDM = new DownLoadManager();
//观察者,也就是回调接口的集合
private static ArrayList<DownLoadObserver> observers = new ArrayList<DownLoadObserver>();

/**
* 下载对象的集合
* 使用线程安全的模式
* @return
*/
private ConcurrentHashMap<Integer,DownLoadInfo> DownLoinfoCHM =  new ConcurrentHashMap<Integer, DownLoadInfo>();

//下载任务的对象的集合
private ConcurrentHashMap<Integer, DownLoadTask> loadTaskCHM = new ConcurrentHashMap<Integer, DownLoadTask>();

//单例模式,饿汉模式
public static DownLoadManager getInstance(){
return mDM;
}

//注册观察者
public void registerObserver(DownLoadObserver observer){
if(observer != null&& !observers.contains(observer)){
observers.add(observer);
}
}

//注销观察者
public void unRegisterObser(DownLoadObserver observer){
if(observer!= null&& observers.contains(observer)){

observers.remove(observer);
}
}

//提醒观察者,刷新数据,下载的状态
public void notifyDownLoadStateChange(DownLoadInfo info){
for (DownLoadObserver observer:observers) {
observer.DOwnLoadStateChange(info);
}
}

//提醒观察者,数据更新了
public void notifytyDownLoadPregress(DownLoadInfo info){
for (DownLoadObserver observer:observers) {
observer.DownLoadProgressChange(info);
}
}

//观察者模式--回调接口
public interface DownLoadObserver{

//下载进度发生改变
void DownLoadProgressChange(DownLoadInfo info);
//DownLoad状态发生改变
void DOwnLoadStateChange(DownLoadInfo info);
}

/**
* 1.下载apk文件
*/
public void download(Appinfo appinfo){

DownLoadInfo downLoadInfo = DownLoinfoCHM.get(appinfo.id);

if(downLoadInfo == null){
downLoadInfo = DownLoadInfo.copy(appinfo);;
}
//修改下载的状态,等待下载
downLoadInfo.currentState = STATE_WAITING;
notifyDownLoadStateChange(downLoadInfo);
//把下载对象信息,加入下载对象集合,保存起来
DownLoinfoCHM.put(downLoadInfo.id,downLoadInfo);

DownLoadTask task = new DownLoadTask(downLoadInfo);

//执行线程池的下载操作,将下载任务添加到队列,并执行
ThreadManager.getInstance().execute(task);
loadTaskCHM.put(appinfo.id,task);

}

/**
* 暂停
* 1.获取DownLoadInfo:
* 2.通过DownLoadInfo.id:获取DownLoadInfo对象
* 3.将DownLoadInfo的下载任务从TashHashMap移除
*
*/
public void pause(Appinfo info){
DownLoadInfo downinfo = DownLoinfoCHM.get(info.id);

if(downinfo.currentState == STATE_DOWNLOADING || downinfo.currentState == STATE_WAITING){
DownLoadTask task = loadTaskCHM.get(downinfo.id);

if (task != null){
//停止下载,经队列从线程池中取消
ThreadManager.getInstance().cancel(task);
//更新状态
downinfo.currentState = STATE_PAUSE;
notifyDownLoadStateChange(downinfo);
}
}

}

/**
* 安装应用
* 调用安装应用的activity
* 自动安装
*/
public void instant(Appinfo info){
DownLoadInfo downloadInfo = DownLoinfoCHM.get(info.id);
if (downloadInfo != null) {
// 跳到系统的安装页面进行安装
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.parse("file://" + downloadInfo.path),
"application/vnd.android.package-archive");
UIUtils.getContext().startActivity(intent);
}
}

/**
* 1.下载应用程序,
* 2.判断线程池是否有过下载记录
* 3.如果有,则判断已经下载的数量是否和DwonloadInfo的信息中记录的是否相同
* 4.如果相同:则执行断点续传操作,接着上述内容,将下载的内容拼接到应用中
* 5.如果不相同,或者已经下载错误,则把以前下载的数据清零,然后删除已经下载的文件,
*/
class DownLoadTask implements Runnable{

private DownLoadInfo dlInfo;

public DownLoadTask(DownLoadInfo info) {
this.dlInfo = info;
}

@Override
public void run() {
//更改状态,然后刷新页面通知
File file = new File(dlInfo.path);

dlInfo.currentState = STATE_DOWNLOADING;
notifyDownLoadStateChange(dlInfo);
if(file.length() == dlInfo.size&&file.length() == dlInfo.currentPos){
dlInfo.currentState = STATE_SUCCESS;
notifytyDownLoadPregress(dlInfo);
}
if (!file.exists() || file.length() != dlInfo.currentPos
|| dlInfo.currentPos == 0) {
// 从头开始下载
// 删除无效文件
file.delete();// 文件如果不存在也是可以删除的, 只不过没有效果而已
dlInfo.currentPos = 0;// 当前下载位置置为0

}

HttpURLConnection connection = null;
RandomAccessFile randomAccessFile = null;
InputStream is = null;
try {

URL url = new URL(HttpHelper.URL + dlInfo.downloadUrl);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
// 设置范围,格式为Range:bytes x-y;
connection.setRequestProperty("Range", "bytes=" + dlInfo.currentPos + "-" + dlInfo.size);

randomAccessFile = new RandomAccessFile(file, "rwd");
randomAccessFile.seek(0 + dlInfo.currentPos);
Log.i("RG", "connection--->>>" + connection);
// 将要下载的文件写到保存在保存路径下的文件中
is = connection.getInputStream();
byte[] buffer = new byte[4096];
int length = -1;
while ((length = is.read(buffer)) != -1  && dlInfo.currentState == STATE_DOWNLOADING) {
randomAccessFile.write(buffer, 0, length);
dlInfo.currentPos += length;
notifytyDownLoadPregress(dlInfo);
}
if(dlInfo.currentPos == dlInfo.size){
dlInfo.currentState = STATE_SUCCESS;
notifytyDownLoadPregress(dlInfo);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
randomAccessFile.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

public DownLoadInfo getDownloadInfo(Appinfo info) {
return DownLoinfoCHM.get(info.id);
}

}


ThreadManager.java

package com.example.orchid.googleplatstore.manager;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
* Created by orchid
* on 16-11-4.
*  线程池类的封装
*
*/

public class ThreadManager {

private static ThreadPool Instance;

//单例模式,获取线程池的实例
public  static ThreadPool getInstance(){
if(Instance == null){
synchronized (ThreadPool.class){
if(Instance == null){
int threadCount = Runtime.getRuntime().availableProcessors();
Instance = new ThreadPool(threadCount,threadCount,1L);
}
}
}
return Instance;
}

public static class ThreadPool{

private  ThreadPoolExecutor executor;
private  int corePoolSize;
private  int maximumPoolSize;
private  long keepAliveTime;

public ThreadPool(int corePoolSize,int maximumPoolSize,long keepAliveTime) {
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.keepAliveTime = keepAliveTime;
}

public  void execute(Runnable r){
if(executor == null){

//线程池执行者。
//参1:核心线程数;参2:最大线程数;参3:线程休眠时间;参4:时间单位;参5:线程队列;参6:生产线程的工厂;参7:线程异常处理策略
executor = new ThreadPoolExecutor(
corePoolSize,maximumPoolSize,keepAliveTime, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
if(executor != null){

executor.execute(r);
}
}
//取消线程
public void cancel(Runnable r){
if(executor != null){
executor.getQueue().remove(r);
}
}
}
}


DownLoadInfo.java

package com.example.orchid.googleplatstore.ui.domin;

import android.os.Environment;

import com.example.orchid.googleplatstore.manager.DownLoadManager;

import java.io.File;

/**
* Created by orchid
* on 16-11-5.
* 下载app的javaBean
*/

public class DownLoadInfo {

public int id;
public String name;
public String downloadUrl;
public long size;
public String packageName;

public long currentPos;// 当前下载位置
public int currentState;// 当前下载状态
public String path;// 下载到本地文件的路径

public static final String GOOGLE_MARKET = "GOOGLE_MARKET";// sdcard根目录文件夹名称
public static final String DONWLOAD = "download";// 子文件夹名称, 存放下载的文件

// 获取下载进度(0-1)
public float getProgress() {
if (size == 0) {
return 0;
}

float progress = currentPos / (float) size;
return progress;
}

/**
* 拷贝对象,
* 从AppInfo中拷贝出一个DownloadInfo,并返回DownLOadInfo
*/

//APpInfo是是下载应用称需使用的信息,用户可以自行封装,只要包含downloadUrl,和 id name即可,
public static DownLoadInfo copy(Appinfo info) {
DownLoadInfo downloadInfo = new DownLoadInfo();

downloadInfo.id = info.id;
downloadInfo.name = info.name;
downloadInfo.downloadUrl = info.downloadUrl;
downloadInfo.packageName = info.packageName;
downloadInfo.size = info.size;

downloadInfo.currentPos = 0;
downloadInfo.currentState = DownLoadManager.STATE_UNDO;// 默认状态是未下载
downloadInfo.path = downloadInfo.getFilePath();
return downloadInfo;
}

/**
*1.获取文件下载路径
*2.动态的拼接出一个文件的下载路径,
*3.使用name作为下载文件的文件名称
*4.通过createDir()判断,文件夹是否存在
* @return 
*          null:获取文件夹路径失败
*          !=null :获取文件夹路径成功
*/
public String getFilePath() {
StringBuffer sb = new StringBuffer();
String sdcard = Environment.getExternalStorageDirectory()
.getAbsolutePath();
sb.append(sdcard);
// sb.append("/");
//这一句等价 == sb.append("/");
sb.append(File.separator);
sb.append(GOOGLE_MARKET);
sb.append(File.separator);
sb.append(DONWLOAD);

if (createDir(sb.toString())) {
// 文件夹存在或者已经创建完成
return sb.toString() + File.separator + name + ".apk";// 返回文件路径
}

return null;
}

/**
*1.如果存在的话,就返回文件夹的路径
*2.如果不存在就创建文件夹,并将返回的结果返回:true:创建成功;false:创建失败
* @param dir
* @return
*/
private boolean createDir(String dir) {
File dirFile = new File(dir);

// 文件夹不存在或者不是一个文件夹
if (!dirFile.exists() || !dirFile.isDirectory()) {
return dirFile.mkdirs();
}

return true;// 文件夹存在
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐