一个多线程断点续传的案例
来源:互联网 发布:java项目视频教程 编辑:程序博客网 时间:2024/06/06 14:13
文件下载在App应用中也用到很多,一般版本更新时多要用的文件下载来进行处理,以前也有看过很多大神有过该方面的博客,今天我也自己来实践一下,写的一般,还请大家多提意见,共同进步。
- 实现的原理
1.多线程下载:
首先通过下载总线程数来划分文件的下载区域:利用int range = fileSize / threadCount;得到每一段下载量;每一段的位置是i * range到(i + 1) * rang - 1,注意最后一段的位置是到filesize - 1;
通过Http协议的Range字段实现下载文件的分段;
通过Java类RandomAccessFile可以实现文件的随机访问,利用seek方法定位的文件的指定位置;
由HttpUrlConnection获取流来进行流的读写,实现文件的存储;
在下载过程中利用Handler来向外传递下载的信息。
2.断点续传:
对于每一个线程利用一个DownloadInfo类来保存下载的信息,每次在下载过程中向数据库更新信息(我也有想过只在下载暂停时进行更新,但那样的话我们的进程被杀掉时信息就无法保存下来)。在进行下载之前去访问数据库是否有记录存在,如果没有执行第一次下载的初始化,如果存在记录但下载文件不存在时,删掉数据库中的记录之后进行第一次下载的初始化,如果有记录且文件存在,则从数据库中取出信息。
- 执行下载操作的类DownloadTask
public DownloadTask(String downloadUrl, int threadNum, String fileptah, Context context) {//没有sd卡的时候,filepath传入“”空值 this.downloadUrl = downloadUrl; this.threadNum = threadNum; this.filePath = fileptah; this.mContext = context; this.downFileName = fileptah+".tmp "; sqlTool = new DownlaodSqlTool(mContext); } @Override public void run() { //super.run(); downloadInfos = sqlTool.getInfos(downloadUrl); if (downloadInfos.size()== 0 ){ initFirst(); } startThread(); } private void initFirst() { try { Log.i(TAG,fiele+"-----------"+"第一次下载"); URL url = new URL(downloadUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); fileSize = connection.getContentLength();//获取文件的长度 File file = new File(downFileName); if (!file.exists()) { file.createNewFile(); } // 本地访问文件 RandomAccessFile accessFile = new RandomAccessFile(file, "rwd"); accessFile.setLength(fileSize); accessFile.close(); connection.disconnect(); // 计算每条线程下载的数据长度 blockSize = (fileSize % threadNum) == 0 ? fileSize / threadNum : fileSize / threadNum + 1; //保存每条线程下载的信息 for (int i = 0; i < threadNum - 1; i++) { //线程id,开始位置,结束位置,完成的下载大小,下载地址 DownloadInfo info = new DownloadInfo(i, i * blockSize, (i + 1) * blockSize - 1, 0, downloadUrl); downloadInfos.add(info); } //保存最后一条线程下载的信息 DownloadInfo info = new DownloadInfo(threadNum - 1, (threadNum - 1) * blockSize, fileSize - 1, 0, downloadUrl); downloadInfos.add(info); sqlTool.insertInfos(downloadInfos);//创建数据 } catch (MalformedURLException e) { e.printStackTrace(); } catch (ProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private void startThread() { //开启多条线程去下载 if (downloadInfos != null) { for (DownloadInfo infos : downloadInfos) { Log.i(TAG,fiele+"-----------"+infos.getThreadId()+"开启下载线程"); FileDownloadThread thread = new FileDownloadThread(this,downFileName,infos); executorService.execute(thread); downloadThreads.add(thread); } } } /** * 暂停下载 */ public void pauseDownload(){ for(FileDownloadThread downloadThread : downloadThreads){ if (downloadThread!=null) { downloadThread.setPause(true); Log.i(TAG,fiele+"-----------"+downloadThread.threadId+"暂停了"); } } } /** * 开始下载 */ public void startDownload(){ downloadThreads.clear(); //downloadInfos.clear(); Log.i(TAG,"网络来了继续开始下"); //initThread(); //run(); startThread(); } @Override public void pauseCallBack(DownloadInfo threadBean) { sqlTool.updataInfos(threadBean.getThreadId(), threadBean.getCompeleteSize(), threadBean.getUrl());//更新下载进度到数据库 } @Override public synchronized void threadDownLoadFinished(DownloadInfo downloadInfo) { for( DownloadInfo info:downloadInfos){ if(info.getThreadId() == downloadInfo.getThreadId()){ //从列表中将已下载完成的线程信息移除 downloadInfos.remove(info); Log.i(TAG,fiele+"-----------"+downloadInfo.getThreadId()+"下载完成了"); break; } } if(downloadInfos.size()==0){//如果列表size为0 则所有线程已下载完成 //删除数据库中的信息 sqlTool.delete(downloadUrl); sqlTool.closeDb(); File file = new File(downFileName); File newf = new File(filePath); file.renameTo(newf); } }
- 执行下载任务的子线程
public FileDownloadThread(DownloadCallBack callBack, String file, DownloadInfo infos) { this.mCallBack =callBack; this.file = file; this.threadId = infos.getThreadId(); this.startPos = infos.getStartPos(); this.endPos = infos.getEndPos(); this.downloadLength = infos.getCompeleteSize(); this.downloadUrl = infos.getUrl(); this.mDownloadInfo =infos; blockSize = endPos - startPos + 1; } @Override public void run() { super.run(); HttpURLConnection connection = null; BufferedInputStream bis = null; RandomAccessFile raf = null; try { connection = (HttpURLConnection) new URL(downloadUrl).openConnection(); connection.setConnectTimeout(5000); connection.setRequestMethod("GET"); connection.setAllowUserInteraction(true); //设置当前线程下载的起点、终点(第一次下载的话,是开始位置+0,后面就是开始位置+已经下载的进度) connection.setRequestProperty("Range", "bytes=" + (startPos + downloadLength) + "-" + endPos); raf = new RandomAccessFile(file, "rwd"); //从文件的指定位置开始读写 raf.seek(startPos + downloadLength); byte[] buffer = new byte[1024]; bis = new BufferedInputStream(connection.getInputStream()); int len = -1; while ((len = bis.read(buffer, 0, 1024)) != -1) { raf.write(buffer, 0, len);//写入文件 downloadLength += len;//已下载的进度叠加 //sqlTool.updataInfos(threadId, downloadLength, downloadUrl);//更新下载进度到数据库 if(mPause){ mCallBack.pauseCallBack(mDownloadInfo); return; } if (downloadLength >= blockSize) {//单个线程下载完成 mCallBack.threadDownLoadFinished(mDownloadInfo); } } } catch (IOException e) { e.printStackTrace(); } finally { try { if (bis != null) { bis.close(); } raf.close(); connection.disconnect(); } catch (IOException e) { e.printStackTrace(); } } } public void setPause(boolean pause) { mPause = pause; }
- 保存下载信息的数据库操作
创建数据库的操作
/** * 利用数据库来记录下载信息 * @author acer */public class DownLoadHelper extends SQLiteOpenHelper{ private static final String SQL_NAME = "download.db"; private static final int DOWNLOAD_VERSION=1; public DownLoadHelper(Context context) { super(context, SQL_NAME, null, DOWNLOAD_VERSION); // TODO Auto-generated constructor stub } /** * 在download.db数据库下创建一个download_info表存储下载信息 */ @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table download_info(_id integer PRIMARY KEY AUTOINCREMENT, thread_id integer, " + "start_pos integer, end_pos integer, compelete_size integer,url char)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }
数据库的增删改查
/** * 创建下载的具体信息 */ public void insertInfos(List<DownloadInfo> infos) { SQLiteDatabase database = dbHelper.getWritableDatabase(); for (DownloadInfo info : infos) { String sql = "insert into download_info(thread_id,start_pos, end_pos,compelete_size,url) values (?,?,?,?,?)"; Object[] bindArgs = { info.getThreadId(), info.getStartPos(), info.getEndPos(), info.getCompeleteSize(), info.getUrl() }; database.execSQL(sql, bindArgs); } } /** * 得到下载具体信息 */ public List<DownloadInfo> getInfos(String urlstr) { List<DownloadInfo> list = new ArrayList<DownloadInfo>(); SQLiteDatabase database = dbHelper.getWritableDatabase(); String sql = "select thread_id, start_pos, end_pos,compelete_size,url from download_info where url=?"; Cursor cursor = database.rawQuery(sql, new String[] { urlstr }); while (cursor.moveToNext()) { DownloadInfo info = new DownloadInfo(cursor.getInt(0), cursor.getInt(1), cursor.getInt(2), cursor.getInt(3), cursor.getString(4)); list.add(info); } return list; } /** * 更新数据库中的下载信息 */ public void updataInfos(int threadId, int compeleteSize, String urlstr) { SQLiteDatabase database = dbHelper.getWritableDatabase(); String sql = "update download_info set compelete_size=? where thread_id=? and url=?"; Object[] bindArgs = { compeleteSize, threadId, urlstr }; database.execSQL(sql, bindArgs); } /** * 关闭数据库 */ public void closeDb() { dbHelper.close(); } /** * 下载完成后删除数据库中的数据 */ public void delete(String url) { SQLiteDatabase database = dbHelper.getWritableDatabase(); database.delete("download_info", "url=?", new String[] { url }); }
- 保存下载信息实体类
public class DownloadInfo { /** * 保存每个下载线程下载信息类 * */private int threadId;// 下载器id private int startPos;// 开始点 private int endPos;// 结束点 private int compeleteSize;// 完成度 private String url;// 下载文件的URL地址 public DownloadInfo(int threadId, int startPos, int endPos, int compeleteSize, String url) { this.threadId = threadId; this.startPos = startPos; this.endPos = endPos; this.compeleteSize = compeleteSize; this.url = url; } public DownloadInfo() { } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public int getThreadId() { return threadId; } public void setThreadId(int threadId) { this.threadId = threadId; } public int getStartPos() { return startPos; } public void setStartPos(int startPos) { this.startPos = startPos; } public int getEndPos() { return endPos; } public void setEndPos(int endPos) { this.endPos = endPos; } public int getCompeleteSize() { return compeleteSize; } public void setCompeleteSize(int compeleteSize) { this.compeleteSize = compeleteSize; } @Override public String toString() { return "DownloadInfo [threadId=" + threadId + ", startPos=" + startPos + ", endPos=" + endPos + ", compeleteSize=" + compeleteSize + "]"; }
- 下载完成的回调
public interface DownloadCallBack { /** * 暂停回调 * @param threadBean */ void pauseCallBack(DownloadInfo threadBean); /** * 线程下载完毕 * @param threadBean */ void threadDownLoadFinished(DownloadInfo threadBean);
Demo下载
阅读全文
0 0
- 一个多线程断点续传的案例
- java多线程的一个案例
- Android多线程断点续传详解与案例
- 多线程断点续传的实现
- 关于一个多线程、断点续传的FTP服务程序!
- 一个Http多线程下载与断点续传的DLL
- 一个精彩的多线程 同步 案例
- JAVA多线程的一个简单案例
- 一个断点续传的代码
- 点对点多线程断点续传的实现
- 点对点多线程断点续传的实现
- 点对点多线程断点续传的实现
- P2P多线程断点续传的实现
- 点对点多线程断点续传的实现
- 点对点多线程断点续传的实现
- 点对点多线程断点续传的实现
- 点对点多线程断点续传的实现
- 使用Java实现多线程下载断点续传功能案例
- 有趣的编程----控制自己电脑的CPU
- Python3 如何优雅地使用正则表达式(详解五)
- 为什么要写blog????
- 聊聊Thrift(三) thrift 服务篇-TThreadPoolServer
- 工作中碰到的git命令总结(会持续更新~~)
- 一个多线程断点续传的案例
- 如何让.gitignore文件生效
- 简述Handler
- iOS判断一段字符串长度(汉字2字节)
- Android源码之单双摄像头修改
- centos 6.5安装redis
- VMware12下Linux网络配置
- Windows程序运行原理
- dos命令提示符