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

什么是断点续传?

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

我写了个小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 }
 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 }

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

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

时间: 2024-11-08 04:12:01

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

安卓 开发笔记目录

安卓 开发笔记index 安卓基础 Fragment总结 安卓 BroadcastReceiver笔记 安卓 Notification-通知总结 开源框架笔记 安卓 okhttp小结 EventBus框架总结 安卓 图片加载框架ImageLoader 第三方SDK 安卓 短信验证MobSMS集成 开源项目 其他 安卓 常用属性设置代码笔记 安卓 代码混淆与打包 AOSP开发笔记 开发工具 SecureCRT连接Ubuntu配置 Ubuntu开发环境搭建 开发环境 AOSP android7.1.

安卓开发笔记——丰富多彩的TextView

随手笔记,记录一些东西~ 记得之前写过一篇文章<安卓开发笔记——个性化TextView(新浪微博)>:http://www.cnblogs.com/lichenwei/p/4411607.html 文章里实现个性化TextView的主要方法是通过替换的方式,对关键字进行一些个性化处理,晚上再来补充一种实现方式. 老规矩,先看下效果图: 晚上带来的这种实现方式是通过Android官方给我们提供的Html类下面的fromHtml方法,这个方法可以对字符串进行HTML格式化,让TextView等一些

安卓开发笔记——打造属于自己的博客园APP(四)

在上篇文章<安卓开发笔记——打造属于自己的博客园APP(三)>中,我们对博客文章的详情页和评论页进行了实现,慢慢的一个APP已经出现雏形了,当然这只是完成了"表面效果",要真正做好一个APP并不是一件很轻松的事情,有很多细节需要我们一点一滴的去完善. 好了,来讲下今天要完成的效果,在优化了之前部分代码的前提下,今天来说下关于博客搜索和博客详情页的实现,依旧国际惯例,来看下效果图:(动态图片比较大,加载需要点时间) 效果比较简单,很多东西我们还是可以复用之前的代码,毕竟这种列

安卓开发笔记——自定义广告轮播Banner(无限循环实现)

关于广告轮播,大家肯定不会陌生,它在现手机市场各大APP出现的频率极高,它的优点在于"不占屏",可以仅用小小的固定空位来展示几个甚至几十个广告条,而且动态效果很好,具有很好的用户"友好性",下面来看几个示例图:     再来看下我仿写的效果: 关于广告轮播Banner这个东西,GitHub上面应该有现成的开源组件,不过我没去找过,觉得实现起来不会太难,就自己去仿写了,下面我说下实现的思路: 1.首先看到这个可以滑动切换图片的界面,我们很自然就会想到ViewPager

安卓开发笔记——多种方式实现底部菜单栏(仿微信界面)

关于底部菜单是什么,我想没必要介绍了,在市场上的APP里太常见了,这里提供两种方式来实现. 记得之前写过几篇关于底部菜单实现的方法,有兴趣的朋友可以看看: 1.<安卓开发复习笔记——TabHost组件(一)(实现底部菜单导航)> 2.<安卓开发复习笔记——TabHost组件(二)(实现底部菜单导航)> 3.<安卓开发笔记——Fragment+FragmentTabHost组件(实现新浪微博底部菜单)> 今天带来种相对更通俗易懂的写法,不再和过去一样去沿用TabHost了

安卓开发笔记——高仿新浪微博文字处理(实现关键字高亮,自定义表情替换并加入点击事件实现)

先让大家看下效果图,这个是我自己在闲暇时间仿写的新浪微博客户端: 今天来讲讲如何实现上图的效果,这里需要用到SpannableString这个工具类,如果你对这个类并不熟悉,可以先看下我之前写的2篇文章: <安卓开发笔记——个性化TextView(新浪微博)>:http://www.cnblogs.com/lichenwei/p/4411607.html <安卓开发笔记——丰富多彩的TextView>:http://www.cnblogs.com/lichenwei/p/46120

安卓开发笔记——深入Activity

在上一篇文章<安卓开发笔记——重识Activity >中,我们了解了Activity生命周期的执行顺序和一些基本的数据保存操作,但如果只知道这些是对于我们的开发需求来说是远远不够的,今天我们继续探索Activity,来了解下关于Activity任务栈和Activity四种启动模式的区别. 为什么需要了解关于Activity的任务栈,其实最直接的体现就是提高用户交互友好性. 举个例子,当我们去浏览一个新闻客户端的时候,我们进入了新闻详情页,在这个页面有相隔两条的新闻标题,当我们去点击这个标题的时

安卓开发笔记——关于Handler的一些总结(上)

接上篇文章<安卓开发笔记——关于AsyncTask的使用>,今天来讲下在安卓开发里"重中之重"的另一个异步操作类Handler. 今天打算先讲下关于Handler的一些基本定义和使用方式 还是以一个下载图片为例,先看下实例效果: 好了,先来看下关于Handler的定义: 以上是官方对于Hanler类的描述,大致意思是说:Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息

C#开发笔记-读取文件,并将文件使用分隔符隔开

//读取报告数据 string data=null; using (System.IO.StreamReader sr = new System.IO.StreamReader(filePath, System.Text.Encoding.Default)) { while ((data = sr.ReadLine()) != null) { // data = sr.ToString(); string[] val=data.Split(','); } } C#开发笔记-读取文件,并将文件使用