总体思路 是这样 还有很多需要优化的地方
Activity
//一个文件 单线程 断点 下载
public class DownLoadDemoActivity extends AppCompatActivity {
private TextView mTvFileName ;
private ProgressBar mPbProgress;
private Button btnStop;
private Button btnStart;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_down_load_demo);
mTvFileName = (TextView) findViewById(R.id.tvFileName);
mPbProgress = (ProgressBar) findViewById(R.id.pbProgress);
btnStart = (Button) findViewById(R.id.btnStart);
btnStop = (Button) findViewById(R.id.btnStop);
mPbProgress.setMax(100);
//创建文件信息 这里使用 知乎的作参考
final FileInfo fileInfo = new FileInfo(0,"http://zhstatic.zhihu.com/pkg/store/zhihu/futureve-mobile-zhihu-release-4.51.1(488).apk","知乎apk",0,0);
mTvFileName.setText(fileInfo.getFileName());
btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(DownLoadDemoActivity.this,DownLoadService.class);
i.setAction(DownLoadService.ACTION_START);
i.putExtra("fileInfo",fileInfo);//传递文件
startService(i);
}
});
btnStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 通过Intent传递参数给Service
Intent intent = new Intent(DownLoadDemoActivity.this, DownLoadService.class);
intent.setAction(DownLoadService.ACTION_STOP);
intent.putExtra("fileInfo", fileInfo);
startService(intent);
}
});
// 注册广播接收器
IntentFilter filter = new IntentFilter();
filter.addAction(DownLoadService.ACTION_UPDATE);
registerReceiver(mReceiver, filter);
}
/**
* 更新UI的广播接收器
*/
BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DownLoadService.ACTION_UPDATE.equals(intent.getAction())) {
int finised = intent.getIntExtra("finished", 0);
mPbProgress.setProgress(finised);
Logger.show("接收到的比例",finised+"%");
}
}
};
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
}
}
下载的Service
/**
* 下载 service
*/
public class DownLoadService extends Service {
public static final String ACTION_START = "ACTION_START";
public static final String ACTION_STOP = "ACTION_STOP";
public static final String ACTION_UPDATE = "ACTION_UPDATE"; //更新进度
public static final String TAG ="DownLoadService";
public static final String DOWNLOAD_PAHT = Environment.getExternalStorageDirectory().getAbsolutePath()+
"/downloads/";
public static final int MSG_INIT =0;
private DownloadTask downloadTask =null;
// 初始化线程(获取文件长度 后 会接受到message)
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case MSG_INIT:
FileInfo fileInfo = (FileInfo) msg.obj;
//启动下载 任务线程
downloadTask = new DownloadTask(DownLoadService.this, fileInfo);
downloadTask.downLoad();
break;
default:
break;
}
}
};
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_START.equals(intent.getAction())){
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
Log.e(TAG,"开始:"+fileInfo.toString());
//启动 初始化 线程
new InitThread(fileInfo).start();
} else if(ACTION_STOP.equals(intent.getAction())){
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
if (downloadTask != null) {
downloadTask.isPause = true;
}
Log.e(TAG,"暂停:"+fileInfo.toString());
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
class InitThread extends Thread{
private FileInfo fileInfo =null;
public InitThread(FileInfo fileInfo) {
this.fileInfo = fileInfo;
}
@Override
public void run() {
HttpURLConnection conn = null;
RandomAccessFile raf = null;
URL url = null;
try {
//连接当前 网络
url = new URL(fileInfo.getUrl());
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(3000);
conn.setRequestMethod("GET"); //这里 用Get进行下载
int length = -1;
if (conn.getResponseCode() == 200){
//获取 文件长度
length = conn.getContentLength();
}
Logger.show("下载长度",""+length+"结果码"+conn.getResponseCode());
if (length<=0){
return;
}
// todo 创建本地 下载路径
File dir = new File(DOWNLOAD_PAHT);
if (!dir.exists()){
dir.mkdir();
}
File file = new File(dir,fileInfo.getFileName());
//在文件的任意一个位置 进行 断点操作 rw 表示读写模式可读可写 可删除
raf = new RandomAccessFile(file, "rwd");
raf.setLength(length); //长度为文件的长度
fileInfo.setLength(length);
//发送 message
mHandler.obtainMessage(MSG_INIT,fileInfo).sendToTarget();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
conn.disconnect();
raf.close(); //关闭流
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
下载任务类 DownloadTask
/**
* Created by dc on 2017/5/23.
* 下载 任务类
*/
public class DownloadTask {
private Context mContext = null;
private FileInfo mFileInfo = null;
private int mFinised = 0; //默认结束位置
private ThreadDaoImpl mDao = null;
public boolean isPause = false;
public DownloadTask(Context mContext, FileInfo mFileInfo) {
this.mContext = mContext;
this.mFileInfo = mFileInfo;
mDao = new ThreadDaoImpl();
}
/**
* 开始下载
*/
public void downLoad() {
// 读取数据库的线程信息
List<ThreadInfo> threads = mDao.getThreads(mFileInfo.getUrl());
ThreadInfo threadInfo = null;
if (0 == threads.size()) {//数据库中无数据
// 初始化线程信息对象
threadInfo = new ThreadInfo(mFileInfo.getId(), mFileInfo.getUrl(),
0, mFileInfo.getLength(), 0);
Logger.show("数据库中无数据 ","数据库中无数据");
} else {//数据库 中有数据
threadInfo = threads.get(0);
Logger.show("数据库中有 "+threads.size(),threadInfo.toString());
}
// 创建子线程进行下载
new DownloadThread(threadInfo).start();
}
/**
* 下载 线程
*/
private class DownloadThread extends Thread {
private ThreadInfo mThreadInfo = null;
/**
* @param mInfo
*/
public DownloadThread(ThreadInfo mInfo) {
this.mThreadInfo = mInfo;
}
@Override
public void run() {
//数据库 中 不存在 插入线程信息
if (!mDao.isExists(mThreadInfo.getUrl(), mThreadInfo.getThread_id())) {
mDao.insertThread(mThreadInfo);
Logger.show("插入数据库 ","插入数据库");
}
HttpURLConnection conn = null;
RandomAccessFile raf = null;
InputStream inputStream = null;
try {
URL url = new URL(mThreadInfo.getUrl());
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
// 设置下载位置
int start = mThreadInfo.getStart() + mThreadInfo.getFinished();
//设置 范围 开始到结束
conn.setRequestProperty("Range",
"bytes=" + start + "-" + mThreadInfo.getEnd());
// 设置文件写入位置
File file = new File(DownLoadService.DOWNLOAD_PAHT,
mFileInfo.getFileName());
raf = new RandomAccessFile(file, "rwd");
//读写的时候 跳过的字节
raf.seek(start);
//todo 开始下载 读取数据 写入文件 发广播 给Activity
//发送进度
Intent intent = new Intent();
intent.setAction(DownLoadService.ACTION_UPDATE);
mFinised += mThreadInfo.getFinished(); //下载进度
// 网络正常 时
if (conn.getResponseCode() == 206) {
// 读取数据
inputStream = conn.getInputStream();
byte[] buf = new byte[1024 * 4];
int len = -1;
long time = System.currentTimeMillis();
while ((len = inputStream.read(buf)) != -1) {
// 写入文件
raf.write(buf, 0, len);
// 把下载进度发送广播给Activity
mFinised += len;
//进度 发送activity todo 防止数组越界 int类型最大值超标
if (System.currentTimeMillis() - time > 500) { //间隔半秒发送一次
time = System.currentTimeMillis();
float precent = ((float)mFinised )* 100 / (float) mThreadInfo.getEnd();
intent.putExtra("finished",(int)precent );//%形式
mContext.sendBroadcast(intent);
}
// 在下载暂停时,保存下载进度 到数据库
if (isPause) {
Logger.show("更新数据库 ","更新数据库");
mDao.updateThread(mThreadInfo.getUrl(), mThreadInfo.getThread_id(), mFinised);
return;
}
float precent = ((float)mFinised )* 100 / (float) mThreadInfo.getEnd();
Logger.show("当前总进度"+mThreadInfo.getEnd(),"已下载:"+mFinised+"百分比"+(int)precent+"%");
}
// 下载完成后 删除线程信息
mDao.deleteThread(mThreadInfo.getUrl(), mThreadInfo.getThread_id());
Logger.show("DownloadTask", "下载完毕");
}
} catch (Exception e) {
e.printStackTrace();
} finally { //关闭所有流
try {
if (conn != null) {
conn.disconnect();
}
if (raf != null) {
raf.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
}
文件信息类
/**
*文件信息
*/
public class FileInfo implements Serializable{
private int id;
private String url;
private String fileName;
private int length;
private int finished;
public FileInfo() {
}
/**
* @param id
* @param url
* @param fileName
* @param length 长度
* @param finished 下载进度
*/
public FileInfo(int id, String url, String fileName, int length, int finished) {
this.id = id;
this.url = url;
this.fileName = fileName;
this.length = length;
this.finished = finished;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getFinished() {
return finished;
}
public void setFinished(int finished) {
this.finished = finished;
}
@Override
public String toString() {
return "FileInfo{" +
"id=" + id +
", url='" + url + '\'' +
", fileName='" + fileName + '\'' +
", length=" + length +
", finished=" + finished +
'}';
}
}
线程任务类
/**
* 线程 数据表
* Created by dc on 2017/5/23.
*
*/
public class ThreadInfo extends DataSupport {
private int id;
private int thread_id;
private String url;
private int start;
private int end;
private int finished;
public ThreadInfo() {
}
/**
* @param thread_id
* @param url
* @param start
* @param end
* @param finished 当前完成 进度
*/
public ThreadInfo(int thread_id, String url, int start, int end, int finished) {
this.thread_id = thread_id;
this.url = url;
this.start = start;
this.end = end;
this.finished = finished;
}
public int getThread_id() {
return thread_id;
}
public void setThread_id(int thread_id) {
this.thread_id = thread_id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
public int getFinished() {
return finished;
}
public void setFinished(int finished) {
this.finished = finished;
}
@Override
public String toString() {
return "ThreadInfo{" +
"id=" + id +
", thread_id=" + thread_id +
", url='" + url + '\'' +
", start=" + start +
", end=" + end +
", finished=" + finished +
'}';
}
}
数据库 帮助类
/**
* Created by dc on 2017/5/23.
* 数据库 帮助类 实现 增删 改查
*
*/
public class ThreadDaoImpl {
public ThreadDaoImpl() {
SQLiteDatabase db = LitePal.getDatabase(); //获取 实例
}
public void insertThread(ThreadInfo threadInfo){
threadInfo.save();
}
public void deleteThread(String url ,int thread_id){
String s = String.valueOf(thread_id);
List<ThreadInfo> list = DataSupport.where("url = ? and thread_id = ?",url,s).find(ThreadInfo.class);
if (list.size()>0){
DataSupport.delete(ThreadInfo.class,list.get(0).getId());
}
}
public void updateThread(String url, int thread_id,int finshed){
String id = String.valueOf(thread_id);
ThreadInfo threadInfo = new ThreadInfo();
threadInfo.setFinished(finshed);
threadInfo.updateAll("url = ? and thread_id = ?", url,id);
}
public List<ThreadInfo> getThreads(String url){
List<ThreadInfo> list = DataSupport.where("url = ? ",url).find(ThreadInfo.class);
return list;
}
public boolean isExists(String url, int thread_id) {
String s = String.valueOf(thread_id);
List<ThreadInfo> list = DataSupport.where("url = ? and thread_id = ?",url,s).find(ThreadInfo.class);
if (list.size()>0){
Logger.show("数据库中存在的数量","查询存在 "+list.size());
return true;
}else {
Logger.show("数据库中不存在该记录","数据库中不存在该记录 "+list.size());
return false;
}
}
}