本文实例讲述了android编程开发实现带进度条和百分比的多线程下载。分享给大家供大家参考,具体如下:
继上一篇《java多线程下载实例详解》之后,可以将它移植到我们的安卓中来,下面是具体实现源码:
downactivity.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
|
package com.example.downloads; import java.io.file; import java.io.ioexception; import java.io.randomaccessfile; import java.net.httpurlconnection; import java.net.malformedurlexception; import java.net.url; import com.example.downloads.utils.downloadthread; import android.os.bundle; import android.os.environment; import android.os.handler; import android.os.message; import android.annotation.suppresslint; import android.app.activity; import android.text.textutils; import android.view.menu; import android.view.view; import android.widget.edittext; import android.widget.progressbar; import android.widget.textview; import android.widget.toast; public class downactivity extends activity { // 声明控件 // 路径与线程数量 public edittext et_url, et_num; // 进度条 public static progressbar pb_thread; // 显示进度的操作 public textview tv_pb; // 线程的数量 public static int threadnum = 3 ; // 每个线程负责下载的大小 public int blocksize; public static int threadcount; // 数量 // 访问的path public string path; public static boolean flag = true ; // 记录进度条的值 public static int pb_count = 0 ; public static handler handler; public static final int textvalue = 1 ; public static int pb_num = 0 ; public static int size = 0 ; @override protected void oncreate(bundle savedinstancestate) { super .oncreate(savedinstancestate); setcontentview(r.layout.activity_down); et_url = (edittext) findviewbyid(r.id.et_path); et_num = (edittext) findviewbyid(r.id.et_threadnum); pb_thread = (progressbar) findviewbyid(r.id.pb_down); tv_pb = (textview) findviewbyid(r.id.tv_pb); handler = new handler() { @suppresslint ( "handlerleak" ) @override public void handlemessage(message msg) { super .handlemessage(msg); switch (msg.what) { case textvalue: system.out.println( "-------" + downactivity.pb_count + "//////" + downactivity.size); // 改变textview pb_num = (downactivity.pb_count * 100 ) / downactivity.size; tv_pb.settext( "当前进度是+" + pb_num + "%" ); break ; default : break ; } } }; } @override public boolean oncreateoptionsmenu(menu menu) { // inflate the menu; this adds items to the action bar if it is present. getmenuinflater().inflate(r.menu.main, menu); return true ; } public void download(view v) { downactivity.flag = true ; downactivity.pb_count = 0 ; path = et_url.gettext().tostring(); string threadnum_et = et_num.gettext().tostring(); if (textutils.isempty(path) || textutils.isempty(threadnum_et)) { toast.maketext( this , "不能为空" , toast.length_long).show(); return ; } toast.maketext( this , "url:" + path + "--" + threadnum_et, toast.length_long).show(); // 转换成数字 threadnum = integer.valueof(threadnum_et); new thread( new runnable() { @override public void run() { try { // 创建出url对象 url url = new url(path); // 创建出 httpurlconnection对象 httpurlconnection httpurlconnection = (httpurlconnection) url .openconnection(); // 设置 发请求发送的方式 httpurlconnection.setrequestmethod( "get" ); // 设置请求是否超时时间 httpurlconnection.setconnecttimeout( 5000 ); // 设置 httpurlconnection .setrequestproperty( "user-agent" , " mozilla/5.0 (compatible; msie 10.0; windows nt 6.2; trident/6.0)" ); // 是否响应成功 if (httpurlconnection.getresponsecode() == 200 ) { // 获取文件的大小 size = httpurlconnection.getcontentlength(); system.out.println( "文件的大小" + size); // 设置进度条的最大值 pb_thread.setmax(size); // 创建文件 //保存到sd卡上 // 首先判断是否拥有sdcard if (environment.getexternalstoragestate().equals( environment.media_mounted)) { // 获取sdcard文件目录对象 file sdfile = environment .getexternalstoragedirectory(); // 创建文件对象 file file = new file(sdfile, "youdao.exe" ); randomaccessfile accessfile = new randomaccessfile( file, "rwd" ); // 设置文件的大小 accessfile.setlength(size); // 每个线程下载的大小 blocksize = size / threadnum; // 开三个线程 操作此文件 for ( int i = 1 ; i <= threadnum; i++) { // 1 2 3 // 计算出每个线程开始的位置 int startsize = (i - 1 ) * blocksize; // 结束位置 int endsize = (i) * blocksize; // 当线程是最后一个线程的时候 if (i == threadnum) { // 判断文件的大小是否大于计算出来的结束位置 if (size > endsize) { // 结束位置 等于 文件的大小 endsize = size; } } // 为每个线程创建一个随机的读取 randomaccessfile threadaccessfile = new randomaccessfile( file, "rwd" ); new thread( new downloadthread(i, threadaccessfile, startsize, endsize, path)).start(); } } } } catch (malformedurlexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } }).start(); } /** * 暂停操作 * * @param v */ public void downpause(view v) { toast.maketext( this , "暂停" , toast.length_long).show(); this .flag = false ; } } |
downloadthread.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
|
package com.example.downloads.utils; import java.io.file; import java.io.fileinputstream; import java.io.fileoutputstream; 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 com.example.downloads.downactivity; import android.os.environment; public class downloadthread implements runnable { public randomaccessfile accessfile; // 每个线程 都拥有一个accessfile的文件对象 线程1 线程2 线程3 // 线程下载文件的起始位置 public int startsize; public int endsize; // 文件下载的path路径 public string path; public int threadid; // 线程的标识 public downloadthread( int threadid, randomaccessfile accessfile, int startsize, int endsize, string path) { this .threadid = threadid; this .accessfile = accessfile; this .startsize = startsize; this .endsize = endsize; this .path = path; } @override public void run() { // 执行run方法 try { // 创建文件到sd卡上去 // 首先判断是否拥有sdcard if (environment.getexternalstoragestate().equals( environment.media_mounted)) { // 获取sdcard文件目录对象 file sdfile = environment.getexternalstoragedirectory(); file threadfile = new file(sdfile, threadid + ".txt" ); if (threadfile.exists()) { // 读取该文件的内容 // 创建文件的输入流对象 fileinputstream fis = new fileinputstream(threadfile); // 采用工具类读取 byte data[] = streamtools.istodata(fis); // 转化成字符串 string threadlen = new string(data); if ((threadlen != null ) && (! "" .equals(threadlen))) { startsize = integer.valueof(threadlen); // 解决 416bug的错误 if (startsize > endsize) { startsize = endsize - 1 ; } } } // 创建文件 // 创建url对象 url url = new url(path); // 创建httpurlconnection对象 httpurlconnection httpurlconnection = (httpurlconnection) url .openconnection(); // 设置请求的头 httpurlconnection.setrequestmethod( "get" ); // 设置请求是否超时时间 httpurlconnection.setconnecttimeout( 5000 ); // 设置 httpurlconnection .setrequestproperty( "user-agent" , " mozilla/5.0 (compatible; msie 10.0; windows nt 6.2; trident/6.0)" ); // 关键的设置 httpurlconnection.setrequestproperty( "range" , "bytes=" + startsize + "-" + endsize); // 输出当前线程 system.out.println( "当前线程" + threadid + " 下载开始位置:" + startsize + " 下载结束位置:" + endsize); // 响应成功 // 设置随机读取文件的 开始位置 accessfile.seek(startsize); // 获取相应流对象 inputstream is = httpurlconnection.getinputstream(); // 创建输出流对象 byte buffer[] = new byte [ 1024 ]; int len = 0 ; int threadtotal = 0 ; // 每个线程下载后保存记录 / while ((len = is.read(buffer)) != - 1 ) { accessfile.write(buffer, 0 , len); threadtotal += len; // 记录你写入的长度 //xml文件 //改变进度条: setprogressbar(len); // 通过文件记录文件下载的长度 fileoutputstream fos = new fileoutputstream(threadfile); fos.write((threadtotal + "" ).getbytes()); fos.flush(); fos.close(); //发送handler消息 downactivity.handler.sendemptymessage(downactivity.textvalue); if (!downactivity.flag){ return ; } } accessfile.close(); is.close(); system.out.println(threadid + "线程执行完毕" ); // 线程操作 synchronized (downactivity. class ) { downactivity.threadcount++; if (downactivity.threadcount >= downactivity.threadnum) { for ( int i = 1 ; i <= downactivity.threadnum; i++) { // 获取sdcard上的文件 file deletefile = new file(sdfile, i + ".txt" ); if (deletefile.exists()) { // 文件删除 deletefile.delete(); } } } } } } catch (malformedurlexception e) { // todo auto-generated catch block e.printstacktrace(); } catch (ioexception e) { // todo auto-generated catch block e.printstacktrace(); } } public synchronized void setprogressbar( int len){ downactivity.pb_count+=len; downactivity.pb_thread.setprogress(downactivity.pb_count); } } |
streamtools.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
|
package com.example.downloads.utils; import java.io.bytearrayoutputstream; import java.io.ioexception; import java.io.inputstream; public class streamtools { public static byte [] istodata(inputstream is) throws ioexception{ // 字节输出流 bytearrayoutputstream bops = new bytearrayoutputstream(); // 读取数据的缓存区 byte buffer[] = new byte [ 1024 ]; // 读取长度的记录 int len = 0 ; // 循环读取 while ((len = is.read(buffer)) != - 1 ) { bops.write(buffer, 0 , len); } // 把读取的内容转换成byte数组 byte data[] = bops.tobytearray(); bops.flush(); bops.close(); is.close(); return data; } } |
strings.xml:
1
2
3
4
5
6
7
8
9
10
11
12
|
<?xml version= "1.0" encoding= "utf-8" ?> <resources> <string name= "app_name" >downloads</string> <string name= "action_settings" >settings</string> <string name= "tv_down" >文件下载的地址</string> <string name= "tv_threadnum" >线程数量</string> <string name= "tv_num" > 0 %</string> <string name= "btn_text" >下载</string> <string name= "btn_pause" >暂停</string> <string name= "et_path" >http: //172.22.64.8:8080/doudou/youdao.exe</string> <string name= "et_threadnum" > 3 </string> </resources> |
布局文件:
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
|
<relativelayout xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools= "http://schemas.android.com/tools" android:layout_width= "match_parent" android:layout_height= "match_parent" android:paddingbottom= "@dimen/activity_vertical_margin" android:paddingleft= "@dimen/activity_horizontal_margin" android:paddingright= "@dimen/activity_horizontal_margin" android:paddingtop= "@dimen/activity_vertical_margin" tools:context= ".downactivity" > <textview android:id= "@+id/textview1" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_alignparentleft= "true" android:layout_alignparenttop= "true" android:text= "@string/tv_down" /> <edittext android:id= "@+id/et_path" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_alignparentleft= "true" android:layout_alignparentright= "true" android:layout_below= "@+id/textview1" android:ems= "10" android:inputtype= "none" android:text= "@string/et_path" > <requestfocus /> </edittext> <textview android:id= "@+id/textview2" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_alignparentleft= "true" android:layout_below= "@+id/et_path" android:text= "@string/tv_threadnum" /> <edittext android:id= "@+id/et_threadnum" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_alignleft= "@+id/textview2" android:layout_alignright= "@+id/et_path" android:layout_below= "@+id/textview2" android:ems= "10" android:inputtype= "number" android:text= "@string/et_threadnum" /> <progressbar android:id= "@+id/pb_down" style= "?android:attr/progressbarstylehorizontal" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_alignleft= "@+id/et_threadnum" android:layout_alignright= "@+id/et_threadnum" android:layout_below= "@+id/et_threadnum" android:layout_margintop= "14dp" /> <textview android:id= "@+id/tv_pb" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_alignright= "@+id/textview1" android:layout_below= "@+id/pb_down" android:layout_margintop= "24dp" android:text= "@string/tv_num" /> <button android:id= "@+id/btn_down" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_alignleft= "@+id/pb_down" android:layout_below= "@+id/tv_pb" android:layout_margintop= "32dp" android:onclick= "download" android:text= "@string/btn_text" /> <button android:id= "@+id/button1" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_alignleft= "@+id/btn_down" android:layout_below= "@+id/btn_down" android:layout_margintop= "16dp" android:onclick= "downpause" android:text= "@string/btn_pause" /> </relativelayout> |
效果如下:
最后要注意的是别忘了在项目清单文件中加入权限:
1
2
3
4
5
|
<!-- sdcard权限 --> <uses-permission android:name= "android.permission.mount_unmount_filesystems" /> <uses-permission android:name= "android.permission.write_external_storage" /> <!-- 访问网络的权限 --> <uses-permission android:name= "android.permission.internet" /> |
希望本文所述对大家android程序设计有所帮助。