http://blog.csdn.net/smbroe/article/details/42270573

文件下载在App应用中也用到很多,一般版本更新时多要用的文件下载来进行处理,以前也有看过很多大神有过该方面的博客,今天我也自己来实践一下,写的一般,还请大家多提意见,共同进步。主要思路:

1.多线程下载:

首先通过下载总线程数来划分文件的下载区域:利用int range = fileSize / threadCount;得到每一段下载量;每一段的位置是i * range到(i + 1) * rang  - 1,注意最后一段的位置是到filesize - 1;

通过Http协议的Range字段实现下载文件的分段;

通过Java类RandomAccessFile可以实现文件的随机访问,利用seek方法定位的文件的指定位置;

由HttpUrlConnection获取流来进行流的读写,实现文件的存储;

在下载过程中利用Handler来向外传递下载的信息。

2.断点续传:

对于每一个线程利用一个DownloadInfo类来保存下载的信息,每次在下载过程中向数据库更新信息(我也有想过只在下载暂停时进行更新,但那样的话我们的进程被杀掉时信息就无法保存下来)。在进行下载之前去访问数据库是否有记录存在,如果没有执行第一次下载的初始化,如果存在记录但下载文件不存在时,删掉数据库中的记录之后进行第一次下载的初始化,如果有记录且文件存在,则从数据库中取出信息。

实现的效果如图,自己封装的类提供了开始,暂停,删除,以及重新下载的方法。还没来得及将工程穿上CSDN,给大家一个百度云盘的下载地址:http://pan.baidu.com/s/1dD1Xo8T

主要类的结构有:

1)DownloadHttpTool:利用Http协议进行网络下载类

2)DownlaodSqlTool, DownLoadHelper:数据库相关操作类

3)DownloadInfo:下载信息保存类

4)DownloadUtil:封装下载方法并提供对外接口的类

贴一下代码:DownloadInfo类

package com.example.test;
public class DownloadInfo {/*** 保存每个下载线程下载信息类* */private int threadId;// 下载器idprivate 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;}@Overridepublic String toString() {return "DownloadInfo [threadId=" + threadId + ", startPos=" + startPos+ ", endPos=" + endPos + ", compeleteSize=" + compeleteSize+ "]";}
}

DownLoadHelper类,创建我们的数据库文件

package com.example.test;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
/*** 利用数据库来记录下载信息* @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表存储下载信息*/@Overridepublic 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)");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
}

DownlaodSqlTool进行数据的插入更新删除等操作

package com.example.test;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
/*** * 数据库操作工具类*/
public class DownlaodSqlTool {private DownLoadHelper dbHelper;public DownlaodSqlTool(Context context) {dbHelper = new DownLoadHelper(context);}/*** 创建下载的具体信息*/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 });}
}

DownloadHttpTool进行网络下载的类

package com.example.test;
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.List;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
public class DownloadHttpTool {/*** * 利用Http协议进行多线程下载具体实践类*/private static final String TAG = DownloadHttpTool.class.getSimpleName();private int threadCount;//线程数量private String urlstr;//URL地址private Context mContext;private Handler mHandler;private List<DownloadInfo> downloadInfos;//保存下载信息的类private String localPath;//目录private String fileName;//文件名private int fileSize;private DownlaodSqlTool sqlTool;//文件信息保存的数据库操作类private enum Download_State {Downloading, Pause, Ready;//利用枚举表示下载的三种状态}private Download_State state = Download_State.Ready;//当前下载状态private int globalCompelete = 0;//所有线程下载的总数public DownloadHttpTool(int threadCount, String urlString,String localPath, String fileName, Context context, Handler handler) {super();this.threadCount = threadCount;this.urlstr = urlString;this.localPath = localPath;this.mContext = context;this.mHandler = handler;this.fileName = fileName;sqlTool = new DownlaodSqlTool(mContext);}//在开始下载之前需要调用ready方法进行配置public void ready() {Log.w(TAG, "ready");globalCompelete = 0;downloadInfos = sqlTool.getInfos(urlstr);if (downloadInfos.size() == 0) {initFirst();} else {File file = new File(localPath + "/" + fileName);if (!file.exists()) {sqlTool.delete(urlstr);initFirst();} else {fileSize = downloadInfos.get(downloadInfos.size() - 1).getEndPos();for (DownloadInfo info : downloadInfos) {globalCompelete += info.getCompeleteSize();}Log.w(TAG, "globalCompelete:::" + globalCompelete);}}}public void start() {Log.w(TAG, "start");if (downloadInfos != null) {if (state == Download_State.Downloading) {return;}state = Download_State.Downloading;for (DownloadInfo info : downloadInfos) {Log.v(TAG, "startThread");new DownloadThread(info.getThreadId(), info.getStartPos(),info.getEndPos(), info.getCompeleteSize(),info.getUrl()).start();}}}public void pause() {state = Download_State.Pause;sqlTool.closeDb();}public void delete(){compelete();File file = new File(localPath + "/" + fileName);file.delete();}public void compelete() {sqlTool.delete(urlstr);sqlTool.closeDb();}public int getFileSize() {return fileSize;}public int getCompeleteSize() {return globalCompelete;}//第一次下载初始化private void initFirst() {Log.w(TAG, "initFirst");try {URL url = new URL(urlstr);HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setConnectTimeout(5000);connection.setRequestMethod("GET");fileSize = connection.getContentLength();Log.w(TAG, "fileSize::" + fileSize);File fileParent = new File(localPath);if (!fileParent.exists()) {fileParent.mkdir();}File file = new File(fileParent, fileName);if (!file.exists()) {file.createNewFile();}// 本地访问文件RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");accessFile.setLength(fileSize);accessFile.close();connection.disconnect();} catch (Exception e) {e.printStackTrace();}int range = fileSize / threadCount;downloadInfos = new ArrayList<DownloadInfo>();for (int i = 0; i < threadCount - 1; i++) {DownloadInfo info = new DownloadInfo(i, i * range, (i + 1) * range- 1, 0, urlstr);downloadInfos.add(info);}DownloadInfo info = new DownloadInfo(threadCount - 1, (threadCount - 1)* range, fileSize - 1, 0, urlstr);downloadInfos.add(info);sqlTool.insertInfos(downloadInfos);}//自定义下载线程private class DownloadThread extends Thread {private int threadId;private int startPos;private int endPos;private int compeleteSize;private String urlstr;private int totalThreadSize;public DownloadThread(int threadId, int startPos, int endPos,int compeleteSize, String urlstr) {this.threadId = threadId;this.startPos = startPos;this.endPos = endPos;totalThreadSize = endPos - startPos + 1;this.urlstr = urlstr;this.compeleteSize = compeleteSize;}@Overridepublic void run() {HttpURLConnection connection = null;RandomAccessFile randomAccessFile = null;InputStream is = null;try {randomAccessFile = new RandomAccessFile(localPath + "/"+ fileName, "rwd");randomAccessFile.seek(startPos + compeleteSize);URL url = new URL(urlstr);connection = (HttpURLConnection) url.openConnection();connection.setConnectTimeout(5000);connection.setRequestMethod("GET");connection.setRequestProperty("Range", "bytes="+ (startPos + compeleteSize) + "-" + endPos);is = connection.getInputStream();byte[] buffer = new byte[1024];int length = -1;while ((length = is.read(buffer)) != -1) {randomAccessFile.write(buffer, 0, length);compeleteSize += length;Message message = Message.obtain();message.what = threadId;message.obj = urlstr;message.arg1 = length;mHandler.sendMessage(message);sqlTool.updataInfos(threadId, compeleteSize, urlstr);Log.w(TAG, "Threadid::" + threadId + "    compelete::"+ compeleteSize + "    total::" + totalThreadSize);if (compeleteSize >= totalThreadSize) {break;}if (state != Download_State.Downloading) {break;}}} catch (Exception e) {e.printStackTrace();} finally {try {if (is != null) {is.close();}randomAccessFile.close();connection.disconnect();} catch (Exception e) {e.printStackTrace();}}}}
}

DownloadUtils提供下载向外接口方法类:

package com.example.test;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
/*** 将下载方法封装在此类* 提供下载,暂停,删除,以及重置的方法*/
public class DownloadUtil {private DownloadHttpTool mDownloadHttpTool;private OnDownloadListener onDownloadListener;private int fileSize;private int downloadedSize = 0;@SuppressLint("HandlerLeak")private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubsuper.handleMessage(msg);int length = msg.arg1;synchronized (this) {//加锁保证已下载的正确性downloadedSize += length;}if (onDownloadListener != null) {onDownloadListener.downloadProgress(downloadedSize);}if (downloadedSize >= fileSize) {mDownloadHttpTool.compelete();if (onDownloadListener != null) {onDownloadListener.downloadEnd();}}}};public DownloadUtil(int threadCount, String filePath, String filename,String urlString, Context context) {mDownloadHttpTool = new DownloadHttpTool(threadCount, urlString,filePath, filename, context, mHandler);}//下载之前首先异步线程调用ready方法获得文件大小信息,之后调用开始方法public void start() {new AsyncTask<Void, Void, Void>() {@Overrideprotected Void doInBackground(Void... arg0) {// TODO Auto-generated method stubmDownloadHttpTool.ready();return null;}@Overrideprotected void onPostExecute(Void result) {// TODO Auto-generated method stubsuper.onPostExecute(result);fileSize = mDownloadHttpTool.getFileSize();downloadedSize = mDownloadHttpTool.getCompeleteSize();Log.w("Tag", "downloadedSize::" + downloadedSize);if (onDownloadListener != null) {onDownloadListener.downloadStart(fileSize);}mDownloadHttpTool.start();}}.execute();}public void pause() {mDownloadHttpTool.pause();}public void delete(){mDownloadHttpTool.delete();}public void reset(){mDownloadHttpTool.delete();start();}public void setOnDownloadListener(OnDownloadListener onDownloadListener) {this.onDownloadListener = onDownloadListener;}//下载回调接口public interface OnDownloadListener {public void downloadStart(int fileSize);public void downloadProgress(int downloadedSize);//记录当前所有线程下总和public void downloadEnd();}
}

在MainActivity

package com.example.test;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.test.DownloadUtil.OnDownloadListener;
public class MainActivity extends FragmentActivity {private static final String TAG = MainActivity.class.getSimpleName();private ProgressBar mProgressBar;private Button start;private Button pause;private Button delete;private Button reset;private TextView total;private int max;private DownloadUtil mDownloadUtil;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);start = (Button) findViewById(R.id.button_start);pause = (Button) findViewById(R.id.button_pause);delete = (Button) findViewById(R.id.button_delete);reset = (Button) findViewById(R.id.button_reset);total = (TextView) findViewById(R.id.textView_total);String urlString = "http://bbra.cn/Uploadfiles/imgs/20110303/fengjin/013.jpg";String localPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/local";mDownloadUtil = new DownloadUtil(2, localPath, "abc.jpg", urlString,this);mDownloadUtil.setOnDownloadListener(new OnDownloadListener() {@Overridepublic void downloadStart(int fileSize) {// TODO Auto-generated method stubLog.w(TAG, "fileSize::" + fileSize);max = fileSize;mProgressBar.setMax(fileSize);}@Overridepublic void downloadProgress(int downloadedSize) {// TODO Auto-generated method stubLog.w(TAG, "Compelete::" + downloadedSize);mProgressBar.setProgress(downloadedSize);total.setText((int) downloadedSize * 100 / max + "%");}@Overridepublic void downloadEnd() {// TODO Auto-generated method stubLog.w(TAG, "ENd");}});start.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubmDownloadUtil.start();}});pause.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubmDownloadUtil.pause();}});delete.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubmDownloadUtil.delete();}});reset.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubmDownloadUtil.reset();}});}
}

转载于:https://blog.51cto.com/kenkao/1659986

【Android】多线程下载加断点续传相关推荐

  1. android 多线程下载,断点续传,线程池

    android 多线程下载,断点续传,线程池 你可以在这里看到这个demo的源码: https://github.com/onlynight/MultiThreadDownloader 效果图 这张效 ...

  2. android多线程下载程序卡死,android 多线程下载与断点续传

    多线程下载: 下载速度更快,服务器对每个线程平分资源,故线程越多,得到的资源越多,下载速度越快. 断点续传: 下载中断,再次下载时从上一次下载结束的位置开始下载,防止重复下载 下载结束后 代码: pa ...

  3. Android 多线程下载以及断点续传

    多线程下载 在日常开发中,我们不可避免的会接到类似这样的需求,下载一个比较大的素材文件或者安装包文件,以此实现APP的自动更新,APP内的素材替换等.由于一般此类文件都比较大,一般会在50M以上,如果 ...

  4. Android多线程下载断点续传

    先上图看卡结果: GITHUB:Android多线程下载断点续传 下载杵这儿 如图所示点击下载就开始下载,点击停止就会停止再次点击下载就会接着下载了. 设计思路是这样的: 首先通过广播将下载信息传递给 ...

  5. 更好的Android多线程下载框架

    /*** 作者:Pich* 原文链接:http://me.woblog.cn/* QQ群:129961195* Github:https://github.com/lifengsofts*/ 概述 为 ...

  6. android多线程下载原理,安卓多线程断点续传下载功能(靠谱第三方组件,原理demo)...

    一,原生的DownloadManager 从Android 2.3(API level 9)开始,Android以Service的方式提供了全局的DownloadManager来系统级地优化处理长时间 ...

  7. android线程池断点续传,Android之多线程下载及断点续传

    今天我们来接触一下多线程下载,当然也包括断点续传,我们可以看到 很多下载器,当开通会员的时候下载东西的速度就变得快了许多,这是为什么呢?这就是跟今天讲的多线程有关系了,其实就是多开了几个线程一起下载罢 ...

  8. *Android 多线程下载 仿下载助手(改进版)

    首先声明一点: 这里的多线程下载 并不是指的 多个线程下载一个 文件,而是 每个线程 负责一个文件.真正的多线程 希望后面能给大家带来.  -------------  欢迎 爱学习的小伙伴 加群   ...

  9. android多线程下载3

    今天跟大家一起分享下android开发中比较难的一个环节,可能很多人看到这个标题就会感觉头很大,的确如果没有良好的编码能力和逻辑思维,这块是很难搞明白的,前面2次总结中已经为大家分享过有关技术的一些基 ...

最新文章

  1. linux深度定制,Linux Deepin 12.06 beta1 发布
  2. 计算机无法搜索程序,电脑搜索功能不能用的解决方法
  3. 在ASP程序设计中在使用Response对象
  4. 【svn】设置过滤文件
  5. c语言2048代码linux,C语言2048小游戏课设(附源码).doc
  6. 基于能量采集的认知无线电功率分配
  7. Nginx搭建视频流媒体服务(直播点播)
  8. shell脚本——系统工具箱(SystemToolbox)
  9. 基于OpenCV视频帧差分的身高检测
  10. @JSONField 注解详解和应用
  11. 关于为什么不能减小rb来消除截止失真
  12. java计算机毕业设计咖啡馆管理系统源程序+mysql+系统+lw文档+远程调试
  13. python中类对象和实例对象_python基础之类和对象、对象之间的交互、类名称空间与对象/实例名称空间...
  14. 修复图片音频全新升级带特效喝酒神器小游戏微信小程序源码下载
  15. excel制作跨职能流程图_如何绘制泳道图(跨职能流程图)
  16. 百度的链接打不开解决办法
  17. 腾讯T2亲自教你!java开发项目分工
  18. Kafka入门教程及安装
  19. chrome修改摄像头权限_如何在Chrome中更改网站的摄像头和麦克风权限
  20. 小程序如何引用阿里巴巴图标

热门文章

  1. kmeans 是Nondeterministic algorithm
  2. mqtt与硬件交互_一个关于小程序Iot的具体实现(MQTT版)
  3. pandas fillna_【Python基础】快速提升效率的6个pandas使用小技巧
  4. layui移动端适配_实战:移动端适配的最佳实践
  5. 【企业管理】摆脱三个依赖
  6. Java基础 JSP九大内置对象
  7. 《系统集成项目管理工程师》必背100个知识点-44应急储备和管理储备
  8. 信息系统项目管理师-项目合同管理考点笔记
  9. 重入锁、死锁、活锁、公平非公平锁……一下子都给你屡清楚了
  10. 组织软件测试中应当避免的几个常见现象