最近做的項目中需要實現斷點下載,即用戶一次下載可以分多次進行,下載過程可以中斷,在目前大多數的帶離線緩存的軟件都是需要實現這一功能。本文闡述了通過sqlite3簡單實現了一個具有斷點下載功能的demo。言歸正傳,開始正文。
設計
數據庫表存儲元數據
dbhelper.java
用于業務存儲的dao
dao.java
抽象下載信息的bean
loadinfo.java
呈現下載信息view
mainactivity.java
存儲下載信息bean
downloadinfo.java
封裝好的下載類
downloader.java
代碼結構
具體實現
下載信息類:downloadinfo.java
這里的代碼很簡單,就是一個用來保存下載信息的類,很簡單,沒啥好說的,具體看注釋
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
package entity; public class downloadinfo { private int threadid; //線程id private int startpos; //下載起始位置 private int endpos; //下載結束位置 private int completesize; //下載完成量 private string url; //資源url public downloadinfo( int tid, int sp, int ep, int csize,string address){ threadid=tid; startpos=sp; endpos=ep; completesize = csize; url=address; } /** * @return the threadid */ public int getthreadid() { return threadid; } /** * @param threadid the threadid to set */ public void setthreadid( int threadid) { this .threadid = threadid; } /** * @return the startpos */ public int getstartpos() { return startpos; } /** * @param startpos the startpos to set */ public void setstartpos( int startpos) { this .startpos = startpos; } /** * @return the endpos */ public int getendpos() { return endpos; } /** * @param endpos the endpos to set */ public void setendpos( int endpos) { this .endpos = endpos; } /** * @return the completesize */ public int getcompletesize() { return completesize; } /** * @param completesize the completesize to set */ public void setcompletesize( int completesize) { this .completesize = completesize; } /** * @return the url */ public string geturl() { return url; } /** * @param url the url to set */ public void seturl(string url) { this .url = url; } @override public string tostring() { // todo auto-generated method stub return "threadid:" +threadid+ ",startpos:" +startpos+ ",endpos:" +endpos+ ",completesize:" +completesize+ ",url:" +url; } } |
數據庫 dbhelper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package db; import android.content.context; import android.database.sqlite.sqlitedatabase; import android.database.sqlite.sqliteopenhelper; public class dbhelper extends sqliteopenhelper { string sql = "create table download_info (id integer primary key autoincrement," + "thread_id integer," + "start_pos integer," + "end_pos integer," + "complete_size integer," + "url char)" ; public dbhelper(context context) { // todo auto-generated constructor stub super (context, "download.db" , null , 1 ); } @override public void oncreate(sqlitedatabase db) { // todo auto-generated method stub db.execsql(sql); } @override public void onupgrade(sqlitedatabase db, int oldversion, int newversion) { // todo auto-generated method stub } } |
數據庫業務管理類 dao.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
package db; import java.util.arraylist; import java.util.list; import android.content.context; import android.database.cursor; import android.database.sqlite.sqlitedatabase; import entity.downloadinfo; public class dao { private dbhelper dbhelper; public dao(context context){ dbhelper = new dbhelper(context); } public boolean isnewtask(string url){ sqlitedatabase db = dbhelper.getreadabledatabase(); string sql = "select count(*) from download_info where url=?" ; cursor cursor = db.rawquery(sql, new string[]{url}); cursor.movetofirst(); int count = cursor.getint( 0 ); cursor.close(); return count == 0 ; } public void saveinfo(list<downloadinfo> infos){ sqlitedatabase db = dbhelper.getwritabledatabase(); string sql = "insert into download_info(thread_id,start_pos,end_pos,complete_size,url) values(?,?,?,?,?)" ; object[] bindargs= null ; for (downloadinfo info:infos){ bindargs= new object[]{info.getthreadid(),info.getstartpos(),info.getendpos(),info.getcompletesize(),info.geturl()}; db.execsql(sql, bindargs); } } public list<downloadinfo> getinfo(string url){ sqlitedatabase db = dbhelper.getreadabledatabase(); list<downloadinfo> infos = new arraylist<downloadinfo>(); string sql = "select thread_id,start_pos,end_pos,complete_size,url from download_info where url=?" ; cursor cursor=db.rawquery(sql, new string[]{url}); while (cursor.movetonext()){ downloadinfo info = new downloadinfo(cursor.getint( 0 ), cursor.getint( 1 ), cursor.getint( 2 ), cursor.getint( 3 ),cursor.getstring( 4 )); infos.add(info); } cursor.close(); return infos; } public void deleteinfo(string url){ sqlitedatabase db = dbhelper.getwritabledatabase(); db.delete( "download_info" , "url=?" , new string[]{url}); db.close(); } public void updateinfo( int completesize, int threadid,string url){ sqlitedatabase db = dbhelper.getwritabledatabase(); string sql = "update download_info set complete_size=? where thread_id=? and url=?" ; db.execsql(sql, new object[]{completesize,threadid,url}); } public void closedb(){ dbhelper.close(); } } |
當前狀態保存類 loadinfo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
package entity; public class loadinfo { private int filesize; private int completesize; private string url; public loadinfo( int fs, int csize,string address){ filesize=fs; completesize = csize; url=address; } /** * @return the filesize */ public int getfilesize() { return filesize; } /** * @param filesize the filesize to set */ public void setfilesize( int filesize) { this .filesize = filesize; } /** * @return the completesize */ public int getcompletesize() { return completesize; } /** * @param completesize the completesize to set */ public void setcompletesize( int completesize) { this .completesize = completesize; } /** * @return the url */ public string geturl() { return url; } /** * @param url the url to set */ public void seturl(string url) { this .url = url; } } |
下載助手類:downloader.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
|
package com.winton.downloadmanager; import java.io.file; import java.io.ioexception; import java.io.inputstream; import java.io.randomaccessfile; import java.net.httpurlconnection; import java.net.malformedurlexception; 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 db.dao; import entity.downloadinfo; import entity.loadinfo; public class downloader { private string url; private string localpath; private int threadcount; private handler mhanler; private dao dao; private int filesize; private list<downloadinfo> infos; private final static int init = 1 ; private final static int downloading = 2 ; private final static int pause = 3 ; private int state = init; public downloader(string address,string lpath, int thcount,context context,handler handler){ url =address; localpath = lpath; threadcount = thcount; mhanler = handler; dao = new dao(context); } public boolean isdownloading(){ return state == downloading; } public loadinfo getdownloadinfos(){ if (isfirstdownload(url)){ init(); int range = filesize/threadcount; infos = new arraylist<downloadinfo>(); for ( int i= 0 ;i<=threadcount- 1 ;i++){ downloadinfo info = new downloadinfo(i, i*range, (i+ 1 )*range- 1 , 0 , url); infos.add(info); } dao.saveinfo(infos); return new loadinfo(filesize, 0 , url); } else { infos = dao.getinfo(url); int size = 0 ; int completesize = 0 ; for (downloadinfo info:infos){ completesize += info.getcompletesize(); size += info.getendpos()-info.getstartpos()+ 1 ; } return new loadinfo(size, completesize, url); } } public boolean isfirstdownload(string url){ return dao.isnewtask(url); } public void init(){ try { url murl = new url( this .url); httpurlconnection connection = (httpurlconnection)murl.openconnection(); connection.setconnecttimeout( 5000 ); connection.setrequestmethod( "get" ); filesize = connection.getcontentlength(); file file = new file(localpath); if (!file.exists()){ file.createnewfile(); } randomaccessfile accessfile = new randomaccessfile(file, "rwd" ); accessfile.setlength(filesize); accessfile.close(); connection.disconnect(); } catch (malformedurlexception e) { // todo auto-generated catch block e.printstacktrace(); } catch (ioexception e) { // todo auto-generated catch block e.printstacktrace(); } } public void download(){ if (infos != null ){ if (state ==downloading){ return ; } state = downloading; for (downloadinfo info:infos){ new downloadthread(info.getthreadid(), info.getstartpos(), info.getendpos(), info.getcompletesize(), info.geturl()).start(); } } } class downloadthread extends thread{ private int threadid; private int startpos; private int endpos; private int completesize; private string url; public downloadthread( int tid, int sp, int ep, int csize,string address) { // todo auto-generated constructor stub threadid=tid; startpos=sp; endpos = ep; completesize = csize; url = address; } @override public void run() { // todo auto-generated method stub httpurlconnection connection = null ; randomaccessfile randomaccessfile = null ; inputstream is = null ; try { url murl = new url(url); connection = (httpurlconnection)murl.openconnection(); connection.setconnecttimeout( 5000 ); connection.setrequestmethod( "get" ); connection.setrequestproperty( "range" , "bytes=" +(startpos+completesize)+ "-" +endpos); randomaccessfile = new randomaccessfile(localpath, "rwd" ); randomaccessfile.seek(startpos+completesize); is=connection.getinputstream(); byte [] buffer = new byte [ 4096 ]; int length =- 1 ; while ((length=is.read(buffer)) != - 1 ){ randomaccessfile.write(buffer, 0 , length); completesize +=length; dao.updateinfo(threadid, completesize, url); message msg = message.obtain(); msg.what= 1 ; msg.obj=url; msg.arg1=length; mhanler.sendmessage(msg); if (state==pause){ return ; } } } catch (malformedurlexception e) { // todo auto-generated catch block e.printstacktrace(); } catch (ioexception e) { // todo auto-generated catch block e.printstacktrace(); } finally { try { is.close(); randomaccessfile.close(); connection.disconnect(); dao.closedb(); } catch (ioexception e) { // todo auto-generated catch block e.printstacktrace(); } } } } public void delete(string url){ dao.deleteinfo(url); } public void reset(){ state=init; } public void pause(){ state=pause; } } |
view呈現類:mainactivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
package com.winton.downloadmanager; import android.app.activity; import android.os.bundle; import android.os.environment; import android.os.handler; import android.os.message; import android.view.view; import android.view.view.onclicklistener; import android.widget.button; import android.widget.progressbar; import android.widget.textview; import android.widget.toast; import entity.loadinfo; public class mainactivity extends activity implements onclicklistener{ private textview name; private progressbar process; private button start,stop; private downloader downloader; //處理下載進度ui的 handler handler = new handler(){ public void handlemessage(android.os.message msg) { if (msg.what== 1 ){ name.settext((string)msg.obj); int lenght = msg.arg1; process.incrementprogressby(lenght); } if (msg.what== 2 ){ int max =msg.arg1; process.setmax(max); toast.maketext(getapplicationcontext(), max+ "" , 1 ).show(); } }; }; @override protected void oncreate(bundle savedinstancestate) { super .oncreate(savedinstancestate); setcontentview(r.layout.activity_main); name=(textview)findviewbyid(r.id.tv_name); process=(progressbar)findviewbyid(r.id.pb_download); start = (button)findviewbyid(r.id.bt_start); stop = (button)findviewbyid(r.id.bt_stop); start.setonclicklistener( this ); stop.setonclicklistener( this ); downloader= new downloader( "http://img4.duitang.com/uploads/item/201206/11/20120611174542_5krmj.jpeg" , environment.getexternalstoragedirectory().getpath()+ "/test1.jpg" , 1 , getapplicationcontext(), handler); } @override public void onclick(view v) { // todo auto-generated method stub if (v==start){ new thread( new runnable() { @override public void run() { // todo auto-generated method stub loadinfo loadinfo = downloader.getdownloadinfos(); message msg =handler.obtainmessage(); msg.what= 2 ; msg.arg1=loadinfo.getfilesize(); handler.sendmessage(msg); downloader.download(); } }).start(); return ; } if (v==stop){ downloader.pause(); return ; } } } |
運行效果
總體比較簡單,基本實現了 斷點下載的功能,而且開啟了多個線程去實現分塊下載,對初學的同學有一定的幫助。
這些代碼我也是從網上各種搜集而來,自己親自動手敲了一遍,并做了一些小的改動,對整個斷點下載的過程有了一個深刻的認識。因此平時要多敲代碼,善于總結,必能有所收獲。