安卓开发笔记——关于文件断点续传

什么是断点续传?

客户端软件断点续传指的是在下载或上传时,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载。用户可以节省时间,节省流量,也提高速度。

我写了个小Demo,先看下实现效果图:

  

断点续传的原理?

断点续传其实并没有那么神秘,它只不过是利用了HTTP传输协议中请求头(REQUEST HEADER)的不同来进行断点续传的。

以下是我刚在百度音乐下载MP3的时候用Firebug截下来的HTTP请求头:

Accept:image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch
Accept-Language:zh-CN,zh;q=0.8
Connection:keep-alive
Cookie:BIDUPSID=6B8AF721169ED82B182A7EE22F75BB87; BAIDUID=6B8AF721169ED82B182A7EE22F75BB87:FG=1; BDUSS=1pWS14dzl6Ry02MVJoN0toT1RlTzRIdkdBRVlsN1JJdG9OVmQ5djAybTJ1a1JWQVFBQUFBJCQAAAAAAAAAAAEAAACkSkgjTXJfTGVlX-fiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALYtHVW2LR1VaW; __xsptplus188=188.1.1428024707.1428024712.2%234%7C%7C%7C%7C%7C%23%23QdAfR9H5KZHSIGakiCWebLQCd6CjKjz5%23; locale=zh; cflag=65279%3A3; BDRCVFR[-z8N-kPXoJt]=I67x6TjHwwYf0; H_PS_PSSID=11099_13386_1439_13425_13075_10902_12953_12868_13320_12691_13410_12722_12737_13085_13325_13203_12835_13161_8498
Host:b.hiphotos.baidu.com
Referer:http://music.baidu.com/song/124380645
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36

这是百度音乐服务器给我返回的信息:

Age:347651
Cache-Control:max-age=31536000
Connection:keep-alive
Content-Length:16659
Content-Type:image/jpeg
Date:Sat, 11 Apr 2015 06:48:45 GMT
Error-Message:OK
ETag:"10487865676202532488"
Expires:Wed, 06 Apr 2016 06:02:50 GMT
Last-Modified:Tue, 07 Apr 2015 05:45:32 GMT
Ohc-Cachable:1
Server:JSP3/2.0.7

这是一般的下载请求,服务器会给我们返回我们请求下载文件的长度(Content-Length),成功状态码为:200。

而断点续传是要从我们之前已经下载好的文件位置继续上一次的传输,顾名思义我们需要告诉服务器我们上一次已经下载到哪里了,所以在我们的HTTP请求头里要额外的包含一条信息,而这也就是断点续传的奥秘所在了。

这条信息为:(RANGE为请求头属性,X为从什么地方开始,Y为到什么地方结束)

RANGE: bytes=X-Y 

例如:

Range : 用于客户端到服务器端的请求,可通过该字段指定下载文件的某一段大小,及其单位。典型的格式如:
Range: bytes=0-499 下载第0-499字节范围的内容
Range: bytes=500-999 下载第500-999字节范围的内容
Range: bytes=-500 下载最后500字节的内容
Range: bytes=500- 下载从第500字节开始到文件结束部分的内容

以上是HTTP需要注意的地方,接下来讲讲Java里需要注意的几个类

关于这个HTTP请求,在Java的Net包下,给我们封装好了一系列的类,我们直接拿来使用就行了。

这是java.net.URLConnection类下的一个方法,用来设置HTTP请求头的,Key对应HTTP请求头属性,Value对应请求头属性的值。

这是java.io.RandomAccessFile类,这个类的实例支持对随机访问文件的读取和写入,这个类里有个很重要的seek(long pos)方法,

这个方法可以在你设置的长度的下一个位置进行写入,例如seek(599),那么系统下一次写入的位置就是该文件的600位置。

既然是断点续传,那么我们自身肯定也要记录断点位置,所以这里数据库也是必不可少的。

具体实现:

好了,了解了上面的知识点后,可以开始动手写程序了。

下面是我写的一个小Demo,用单线程来实现的断点续传的,注释非常全。

 这是Demo的结构图:(实在不会画图,凑合着看哈)

主类(Activity):

  1 package com.example.ui;
  2 
  3 import android.app.Activity;
  4 import android.app.AlertDialog;
  5 import android.app.ProgressDialog;
  6 import android.app.AlertDialog.Builder;
  7 import android.content.BroadcastReceiver;
  8 import android.content.Context;
  9 import android.content.DialogInterface;
 10 import android.content.Intent;
 11 import android.content.IntentFilter;
 12 import android.os.Bundle;
 13 import android.util.Log;
 14 import android.view.View;
 15 import android.view.View.OnClickListener;
 16 import android.widget.Button;
 17 import android.widget.ProgressBar;
 18 import android.widget.TextView;
 19 
 20 import com.example.downloadfiletest.R;
 21 import com.example.entity.FileInfo;
 22 import com.example.logic.DownloadService;
 23 
 24 public class MainActivity extends Activity {
 25 
 26     private TextView textView;
 27     private ProgressBar progressBar;
 28     private Button bt_start;
 29     private Button bt_stop;
 30 
 31     @Override
 32     protected void onCreate(Bundle savedInstanceState) {
 33         super.onCreate(savedInstanceState);
 34         setContentView(R.layout.activity_main);
 35         initView(); // 初始化控件
 36 
 37         // 注册广播接收者
 38         IntentFilter intentFilter = new IntentFilter();
 39         intentFilter.addAction(DownloadService.UPDATE);
 40         registerReceiver(broadcastReceiver, intentFilter);
 41 
 42     }
 43 
 44     @Override
 45     protected void onDestroy() {
 46         super.onDestroy();
 47         // 解绑
 48         unregisterReceiver(broadcastReceiver);
 49     }
 50 
 51     // 初始化控件
 52     private void initView() {
 53         textView = (TextView) findViewById(R.id.textView);
 54         progressBar = (ProgressBar) findViewById(R.id.progressBar);
 55         bt_start = (Button) findViewById(R.id.bt_start);
 56         bt_stop = (Button) findViewById(R.id.bt_stop);
 57         progressBar.setMax(100);
 58 
 59         final FileInfo fileInfo = new FileInfo(0, "Best Of Joy.MP3-Michael Jackson", "http://music.baidu.com/data/music/file?link=http://yinyueshiting.baidu.com/data2/music/38264529/382643441428735661128.mp3?xcode=46e7c02e3acba184b6145f688bb9f2422c866f9e4969f410&song_id=38264344", 0, 0);
 60 
 61         // 点击开始下载
 62         bt_start.setOnClickListener(new OnClickListener() {
 63 
 64             @Override
 65             public void onClick(View v) {
 66                 Intent intent = new Intent(MainActivity.this, DownloadService.class);
 67                 intent.setAction(DownloadService.START);
 68                 intent.putExtra("FileInfo", fileInfo);
 69                 startService(intent);
 70                 textView.setText("正在下载文件:" + fileInfo.getFileName());
 71 
 72             }
 73         });
 74 
 75         // 点击停止下载
 76         bt_stop.setOnClickListener(new OnClickListener() {
 77 
 78             @Override
 79             public void onClick(View v) {
 80                 Intent intent = new Intent(MainActivity.this, DownloadService.class);
 81                 intent.setAction(DownloadService.STOP);
 82                 intent.putExtra("FileInfo", fileInfo);
 83                 startService(intent);
 84                 textView.setText("任务已暂停,请点击下载继续");
 85             }
 86         });
 87 
 88     }
 89 
 90     // 广播接收者
 91     BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
 92 
 93         @Override
 94         public void onReceive(Context context, Intent intent) {
 95             if (intent.getAction().equals(DownloadService.UPDATE)) {
 96                 int finished = intent.getIntExtra("finished", 0);
 97                 progressBar.setProgress(finished);
 98                 
 99                 //用户界面友好,提醒用户任务下载完成
100                 if (finished ==100) {
101                     AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this);
102                     builder.setTitle("任务状态");
103                     builder.setMessage("文件下载已完成!");
104                     builder.setPositiveButton("确认", new DialogInterface.OnClickListener() {
105                         
106                         @Override
107                         public void onClick(DialogInterface dialog, int which) {
108                                     progressBar.setProgress(0);
109                                     textView.setText("请点击下载");
110                         }
111                     });
112                     builder.show();
113                 }
114             }
115         }
116     };
117 }

后台服务类(Service)

  1 package com.example.logic;
  2 
  3 import java.io.File;
  4 import java.io.IOException;
  5 import java.io.RandomAccessFile;
  6 import java.net.HttpURLConnection;
  7 import java.net.URL;
  8 
  9 import org.apache.http.HttpStatus;
 10 import org.apache.http.client.ClientProtocolException;
 11 
 12 import android.app.Service;
 13 import android.content.Intent;
 14 import android.os.Environment;
 15 import android.os.Handler;
 16 import android.os.IBinder;
 17 import android.util.Log;
 18 
 19 import com.example.entity.FileInfo;
 20 
 21 public class DownloadService extends Service {
 22 
 23     // 按钮标志符
 24     public static final String START = "START";
 25     public static final String STOP = "STOP";
 26     // 更新进度标志
 27     public static final String UPDATE = "UPDATE";
 28     // 下载路径(内存卡(SD)根目录下的/downloads/)
 29     public static final String DOWNLOADPATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/downloads/";
 30     // 定义初始化文件操作标志
 31     public static final int INIT = 0;
 32 
 33     private DownloadTask downloadTask;
 34 
 35     private Handler handler = new Handler() {
 36         public void handleMessage(android.os.Message msg) {
 37             switch (msg.what) {
 38             case INIT:
 39                 FileInfo fileInfo = (FileInfo) msg.obj;
 40                 Log.i("init", fileInfo.toString());
 41                 // 进行下载任务操作
 42                 downloadTask = new DownloadTask(DownloadService.this, fileInfo);
 43                 downloadTask.download();
 44                 break;
 45             }
 46         };
 47     };
 48 
 49     /**
 50      * 当Service启动时会被调用,用来接收Activity传送过来的数据
 51      */
 52     @Override
 53     public int onStartCommand(Intent intent, int flags, int startId) {
 54         if (intent.getAction().equals(START)) {
 55             // 当点击开始下载操作时
 56             // 接收Activity(putExtra)过来的数据
 57             FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("FileInfo");
 58             Log.i(START, fileInfo.toString());
 59             new Thread(new InitFileThread(fileInfo)).start();
 60 
 61         } else if (intent.getAction().equals(STOP)) {
 62             // 当点击停止下载操作时
 63             FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("FileInfo");
 64             Log.i(STOP, fileInfo.toString());
 65             // 暂定任务
 66             if (downloadTask != null) {
 67                 downloadTask.flag = true;
 68             }
 69         }
 70 
 71         return super.onStartCommand(intent, flags, startId);
 72     }
 73 
 74     @Override
 75     public IBinder onBind(Intent intent) {
 76         return null;
 77     }
 78 
 79     // 初始化文件操作获取网络资源大小长度,开辟子线程
 80     class InitFileThread implements Runnable {
 81 
 82         private FileInfo fileInfo;
 83 
 84         // 构造方法,获取文件对象
 85         public InitFileThread(FileInfo fileInfo) {
 86             this.fileInfo = fileInfo;
 87         }
 88 
 89         @Override
 90         public void run() {
 91             /*
 92              * 1、打开网络连接,获取文件长度 2、创建本地文件,长度和网络文件相等
 93              */
 94             HttpURLConnection httpURLConnection = null;
 95             RandomAccessFile randomAccessFile = null;
 96             try {
 97                 URL url = new URL(fileInfo.getUrl());
 98                 httpURLConnection = (HttpURLConnection) url.openConnection();
 99                 // 知识点:除了下载文件,其他一律用POST
100                 httpURLConnection.setConnectTimeout(3000);
101                 httpURLConnection.setRequestMethod("GET");
102                 // 定义文件长度
103                 int length = -1;
104                 // 网络连接成功
105                 if (httpURLConnection.getResponseCode() == HttpStatus.SC_OK) {
106                     length = httpURLConnection.getContentLength();
107                 }
108                 // 判断是否取得文件长度
109                 if (length <= 0) {
110                     return;
111                 }
112 
113                 // 创建文件目录对象
114                 File dir = new File(DOWNLOADPATH);
115                 if (!dir.exists()) {
116                     // 若目录不存在,创建
117                     dir.mkdir();
118                 }
119                 // 创建文件对象
120                 File file = new File(dir, fileInfo.getFileName());
121                 // 创建随机访问文件流 参数二为权限:读写删
122                 randomAccessFile = new RandomAccessFile(file, "rwd");
123                 randomAccessFile.setLength(length);
124                 fileInfo.setLength(length);
125                 // 发送handler
126                 handler.obtainMessage(INIT, fileInfo).sendToTarget();
127 
128             } catch (ClientProtocolException e) {
129                 e.printStackTrace();
130             } catch (IOException e) {
131                 e.printStackTrace();
132             } finally {
133                 if (randomAccessFile != null) {
134                     try {
135                         randomAccessFile.close();
136                     } catch (IOException e) {
137                         e.printStackTrace();
138                     }
139                 }
140             }
141 
142         }
143 
144     }
145 
146 }

下载任务类:

  1 package com.example.logic;
  2 
  3 import java.io.File;
  4 import java.io.IOException;
  5 import java.io.InputStream;
  6 import java.io.RandomAccessFile;
  7 import java.net.HttpURLConnection;
  8 import java.net.MalformedURLException;
  9 import java.net.URL;
 10 import java.util.List;
 11 
 12 import org.apache.http.HttpStatus;
 13 
 14 import android.content.Context;
 15 import android.content.Intent;
 16 import android.util.Log;
 17 
 18 import com.example.dao.ThreadDAO;
 19 import com.example.dao.ThreadDAOImpl;
 20 import com.example.entity.FileInfo;
 21 import com.example.entity.ThreadInfo;
 22 
 23 /**
 24  * 下载任务类
 25  * 
 26  * @author Balla_兔子
 27  * 
 28  */
 29 public class DownloadTask {
 30     private Context context;
 31     private ThreadDAO dao;
 32     private FileInfo fileInfo;
 33     // 初始化下载进度,默认为0
 34     private int finished = 0;
 35 
 36     // 是否暂停下载标识符
 37     public boolean flag = false;
 38 
 39     public DownloadTask(Context context, FileInfo fileInfo) {
 40         this.context = context;
 41         this.fileInfo = fileInfo;
 42         dao = new ThreadDAOImpl(context);
 43     }
 44 
 45     public void download() {
 46         // 线程信息的url和文件的url对应
 47         List<ThreadInfo> threadInfos = dao.getThreadInfo(fileInfo.getUrl());
 48         ThreadInfo threadInfo = null;
 49         if (threadInfos.size() == 0) {
 50             // 若数据库无此线程任务
 51             threadInfo = new ThreadInfo(0, fileInfo.getUrl(), 0, fileInfo.getLength(), 0);
 52         } else {
 53             threadInfo = threadInfos.get(0);
 54         }
 55         // 创建子线程进行下载
 56         new Thread(new DownloadThread(threadInfo)).start();
 57     }
 58 
 59     // 执行下载任务,开辟子线程
 60     class DownloadThread implements Runnable {
 61 
 62         private ThreadInfo threadInfo;
 63 
 64         public DownloadThread(ThreadInfo threadInfo) {
 65             this.threadInfo = threadInfo;
 66         }
 67 
 68         @Override
 69         public void run() {
 70             HttpURLConnection urlConnection = null;
 71             InputStream inputStream = null;
 72             RandomAccessFile randomAccessFile = null;
 73 
 74             /**
 75              * 执行下载任务 
 76              * 1、查询数据库,确定是否已存在此下载线程,便于继续下载 
 77              * 2、设置从哪个位置开始下载 
 78              * 3、设置文件的写入位置
 79              * 4、开始下载 
 80              * 5、广播通知UI更新下载进度 
 81              * 6、暂停线程的操作 
 82              * 7、下载完毕,删除数据库信息
 83              */
 84             // 1、查询数据库
 85             if (!dao.isExists(threadInfo.getUrl(), threadInfo.getThread_id())) {
 86                 // 若不存在,插入新线程信息
 87                 dao.insertThread(threadInfo);
 88             }
 89 
 90             // 2、设置下载位置
 91             try {
 92                 URL url = new URL(threadInfo.getUrl());
 93                 urlConnection = (HttpURLConnection) url.openConnection();
 94                 // 设置连接超时时间
 95                 urlConnection.setConnectTimeout(3000);
 96                 urlConnection.setRequestMethod("GET");
 97 
 98                 // 设置请求属性
 99                 // 参数一:Range头域可以请求实体的一个或者多个子范围(一半用于断点续传),如果用户的请求中含有range
100                 // ,则服务器的相应代码为206。
101                 // 参数二:表示请求的范围:比如头500个字节:bytes=0-499
102 
103                 // 获取线程已经下载的进度
104                 int start = threadInfo.getStart() + threadInfo.getFinished();
105                 urlConnection.setRequestProperty("range", "bytes=" + start + "-" + threadInfo.getEnd());
106 
107                 // 3、设置文件的写入位置
108                 File file = new File(DownloadService.DOWNLOADPATH, fileInfo.getFileName());
109                 randomAccessFile = new RandomAccessFile(file, "rwd");
110                 // 设置从哪里开始写入,如参数为100,那就从101开始写入
111                 randomAccessFile.seek(start);
112 
113                 finished += threadInfo.getFinished();
114                 Intent intent = new Intent(DownloadService.UPDATE);
115                 // 4、开始下载
116                 if (urlConnection.getResponseCode() == HttpStatus.SC_PARTIAL_CONTENT) {
117                     inputStream = urlConnection.getInputStream();
118                     // 设置字节数组缓冲区
119                     byte[] data = new byte[1024*4];
120                     // 读取长度
121                     int len = -1;
122                     // 取得当前时间
123                     long time = System.currentTimeMillis();
124                     while ((len = inputStream.read(data)) != -1) {
125                         // 读取成功,写入文件
126                         randomAccessFile.write(data, 0, len);
127 
128                         // 避免更新过快,减缓主线程压力,让其0.5秒发送一次进度
129                         if (System.currentTimeMillis() - time > 500) {
130                             time = System.currentTimeMillis();
131                             // 把当前进度通过广播传递给UI
132                             finished += len;
133                             Log.i("finished:", finished+"");
134                             Log.i("file:", fileInfo.getLength()+"");
135                             intent.putExtra("finished", finished*100 / fileInfo.getLength());
136                             context.sendBroadcast(intent);
137                         }
138 
139                         if (flag) {
140                             // 暂停下载,更新进度到数据库
141                             dao.updateThread(threadInfo.getUrl(), threadInfo.getThread_id(), finished);
142                             // 结束线程
143                             return;
144                         }
145 
146                     }
147                     // 当下载执行完毕时,删除数据库线程信息
148                     dao.deleteThread(threadInfo.getUrl(), threadInfo.getThread_id());
149                 }
150 
151             } catch (MalformedURLException e) {
152                 e.printStackTrace();
153             } catch (IOException e) {
154                 e.printStackTrace();
155             } finally {
156                 if (urlConnection != null) {
157                     urlConnection.disconnect();
158                 }
159                 if (inputStream != null) {
160                     try {
161                         inputStream.close();
162                     } catch (IOException e) {
163                         e.printStackTrace();
164                     }
165                 }
166                 if (randomAccessFile != null) {
167                     try {
168                         randomAccessFile.close();
169                     } catch (IOException e) {
170                         e.printStackTrace();
171                     }
172                 }
173 
174             }
175 
176         }
177     }
178 }

数据库帮助类:

 1 package com.example.db;
 2 
 3 import android.content.Context;
 4 import android.database.sqlite.SQLiteDatabase;
 5 import android.database.sqlite.SQLiteOpenHelper;
 6 
 7 public class DBHelper extends SQLiteOpenHelper {
 8     public static final String DBNAME = "download.db";
 9     public static final int VERSION = 1;
10     public static final String TABLE="threadinfo";
11     public static final String CREATE_DB = "create table threadinfo (_id integer primary key autoincrement,thread_id integer,url text,start integer,end integer,finished integer) ";
12     public static final String DROP_DB = "drop table if exists threadinfo";
13 
14     public DBHelper(Context context) {
15         super(context, DBNAME, null, VERSION);
16     }
17 
18     @Override
19     public void onCreate(SQLiteDatabase db) {
20         db.execSQL(CREATE_DB);
21 
22     }
23 
24     @Override
25     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
26         db.execSQL(DROP_DB);
27         db.execSQL(CREATE_DB);
28     }
29 
30 }

数据库表接口类:

 1 package com.example.dao;
 2 
 3 import java.util.List;
 4 
 5 import com.example.entity.ThreadInfo;
 6 
 7 public interface ThreadDAO {
 8     // 新增一条线程信息
 9     public void insertThread(ThreadInfo threadInfo);
10 
11     // 删除一条线程信息(多线程下载,可能一个url对应多个线程,所以需要2个条件)
12     public void deleteThread(String url, int thread_id);
13 
14     // 修改一条线程信息
15     public void updateThread(String url, int thread_id, int finished);
16 
17     // 查询线程有关信息(根据url查询下载该url的所有线程信息)
18     public List<ThreadInfo> getThreadInfo(String url);
19 
20     // 判断线程是否已经存在
21     public boolean isExists(String url, int thread_id);
22 
23 
24 }

数据库表接口实现类:

 1 package com.example.dao;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 import android.content.ContentValues;
 7 import android.content.Context;
 8 import android.database.Cursor;
 9 import android.database.sqlite.SQLiteDatabase;
10 
11 import com.example.db.DBHelper;
12 import com.example.entity.ThreadInfo;
13 
14 /**
15  * ThreadDAO接口实现类
16  * 
17  * @author Balla_兔子
18  * 
19  */
20 public class ThreadDAOImpl implements ThreadDAO {
21 
22     private DBHelper dbHelper;
23 
24     public ThreadDAOImpl(Context context) {
25         dbHelper = new DBHelper(context);
26     }
27 
28     @Override
29     public void insertThread(ThreadInfo threadInfo) {
30         SQLiteDatabase db = dbHelper.getWritableDatabase();
31         ContentValues values = new ContentValues();
32         values.put("thread_id", threadInfo.getThread_id());
33         values.put("url ", threadInfo.getUrl());
34         values.put("start ", threadInfo.getStart());
35         values.put("end ", threadInfo.getEnd());
36         values.put("finished ", threadInfo.getFinished());
37         db.insert(DBHelper.TABLE, null, values);
38         db.close();
39     }
40 
41     @Override
42     public void deleteThread(String url, int thread_id) {
43         SQLiteDatabase db = dbHelper.getWritableDatabase();
44         db.delete(DBHelper.TABLE, "url=? and thread_id=?", new String[] { url, String.valueOf(thread_id) });
45         db.close();
46     }
47 
48     @Override
49     public void updateThread(String url, int thread_id, int finished) {
50         SQLiteDatabase db = dbHelper.getWritableDatabase();
51         db.execSQL("update threadinfo set finished = ? where url = ? and thread_id=?", new Object[] { finished, url, thread_id });
52         db.close();
53     }
54 
55     @Override
56     public List<ThreadInfo> getThreadInfo(String url) {
57         List<ThreadInfo> list = new ArrayList<ThreadInfo>();
58         SQLiteDatabase db = dbHelper.getWritableDatabase();
59         Cursor cursor = db.query(DBHelper.TABLE, null, "url=?", new String[] { url }, null, null, null);
60         while (cursor.moveToNext()) {
61             ThreadInfo threadInfo = new ThreadInfo();
62             threadInfo.setThread_id(cursor.getInt(cursor.getColumnIndex("thread_id")));
63             threadInfo.setUrl(cursor.getString(cursor.getColumnIndex("url")));
64             threadInfo.setStart(cursor.getInt(cursor.getColumnIndex("start")));
65             threadInfo.setEnd(cursor.getInt(cursor.getColumnIndex("end")));
66             threadInfo.setFinished(cursor.getInt(cursor.getColumnIndex("finished")));
67             list.add(threadInfo);
68         }
69         cursor.close();
70         db.close();
71         return list;
72     }
73 
74     @Override
75     public boolean isExists(String url, int thread_id) {
76         SQLiteDatabase db = dbHelper.getWritableDatabase();
77         Cursor cursor = db.query(DBHelper.TABLE, null, "url=? and thread_id=?", new String[] { url, String.valueOf(thread_id) }, null, null, null);
78         boolean isExists = cursor.moveToNext();
79         db.close();
80         return isExists;
81     }
82 
83 }

实体类:(文件,线程)

文件实体类:

 1 package com.example.entity;
 2 
 3 import java.io.Serializable;
 4 
 5 public class FileInfo implements Serializable {
 6 
 7     private int id;
 8     private String fileName;
 9     private String url;
10     private int length;
11     private int finished;
12 
13     public FileInfo() {
14     }
15 
16     public FileInfo(int id, String fileName, String url, int length,
17             int finished) {
18         super();
19         this.id = id;
20         this.fileName = fileName;
21         this.url = url;
22         this.length = length;
23         this.finished = finished;
24     }
25 
26     public int getId() {
27         return id;
28     }
29 
30     public void setId(int id) {
31         this.id = id;
32     }
33 
34     public String getFileName() {
35         return fileName;
36     }
37 
38     public void setFileName(String fileName) {
39         this.fileName = fileName;
40     }
41 
42     public String getUrl() {
43         return url;
44     }
45 
46     public void setUrl(String url) {
47         this.url = url;
48     }
49 
50     public int getLength() {
51         return length;
52     }
53 
54     public void setLength(int length) {
55         this.length = length;
56     }
57 
58     public int getFinished() {
59         return finished;
60     }
61 
62     public void setFinished(int finished) {
63         this.finished = finished;
64     }
65 
66     @Override
67     public String toString() {
68         return "FileInfo [id=" + id + ", fileName=" + fileName + ", url=" + url
69                 + ", length=" + length + ", finished=" + finished + "]";
70     }
71 
72 }
View Code

线程实体类:

 1 package com.example.entity;
 2 
 3 import java.io.Serializable;
 4 
 5 public class ThreadInfo implements Serializable {
 6 
 7     private int thread_id;
 8     private String url;
 9     private int start;
10     private int end;
11     private int finished;
12 
13     public ThreadInfo() {
14     }
15 
16     public ThreadInfo(int thread_id, String url, int start, int end, int finished) {
17         super();
18         this.thread_id = thread_id;
19         this.url = url;
20         this.start = start;
21         this.end = end;
22         this.finished = finished;
23     }
24 
25     public int getThread_id() {
26         return thread_id;
27     }
28 
29     public void setThread_id(int thread_id) {
30         this.thread_id = thread_id;
31     }
32 
33     public String getUrl() {
34         return url;
35     }
36 
37     public void setUrl(String url) {
38         this.url = url;
39     }
40 
41     public int getStart() {
42         return start;
43     }
44 
45     public void setStart(int start) {
46         this.start = start;
47     }
48 
49     public int getEnd() {
50         return end;
51     }
52 
53     public void setEnd(int end) {
54         this.end = end;
55     }
56 
57     public int getFinished() {
58         return finished;
59     }
60 
61     public void setFinished(int finished) {
62         this.finished = finished;
63     }
64 
65     @Override
66     public String toString() {
67         return "ThreadInfo [thread_id=" + thread_id + ", url=" + url + ", start=" + start + ", end=" + end + ", finished=" + finished + "]";
68     }
69 
70 }
View Code

关于多线程的断点续传,其实原理差不多,还是一样把一个文件分成不同区域,然后每个区域各用一条线程去执行下载任务,然后再把文件合并,改天有时间再上个Demo给大家看吧。

 

作者:Balla_兔子
出处:http://www.cnblogs.com/lichenwei/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!

原文地址:https://www.cnblogs.com/lichenwei/p/4417765.html