Android实现多线程下载文件的方法

时间:2022-09-18 15:39:41

本文实例讲述了android实现多线程下载文件的方法。分享给大家供大家参考。具体如下:

多线程下载大概思路就是通过range 属性实现文件分段,然后用randomaccessfile 来读写文件,最终合并为一个文件

首先看下效果图:

Android实现多线程下载文件的方法

创建工程 threaddemo

首先布局文件 threaddemo.xml

?
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
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  >
<textview 
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:text="下载地址"
  />
<textview
  android:id="@+id/downloadurl"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:lines="5"
  />
<textview 
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:text="线程数"
  />
<edittext
  android:id="@+id/downloadnum"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  />
<progressbar
  android:id="@+id/downloadprogressbar"
  android:layout_width="fill_parent"
  style="?android:attr/progressbarstylehorizontal"
  android:layout_height="wrap_content"
  />
<textview
  android:id="@+id/downloadinfo"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:text="下载进度 0"
  />
<button
  android:id="@+id/downloadbutton"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="开始下载"
  />
</linearlayout>
?
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"  android:orientation="vertical" android:layout_width="fill_parent"  android:layout_height="fill_parent"  >
<textview   android:layout_width="fill_parent"   android:layout_height="wrap_content" android:text="下载地址"  />
<textviewandroid:id="@+id/downloadurl"android:layout_width="fill_parent" android:layout_height="wrap_content" android:lines="5"/>
<textview   android:layout_width="fill_parent"   android:layout_height="wrap_content" android:text="线程数"  />
<edittextandroid:id="@+id/downloadnum"android:layout_width="fill_parent" android:layout_height="wrap_content" />
<progressbarandroid:id="@+id/downloadprogressbar"android:layout_width="fill_parent" style="?android:attr/progressbarstylehorizontal"  android:layout_height="wrap_content" />
<textviewandroid:id="@+id/downloadinfo"android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="下载进度 0"/>
<buttonandroid:id="@+id/downloadbutton"android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="开始下载"/>
</linearlayout>

主界面 acitivity

?
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
public class threaddownloaddemo extends activity {
  private textview downloadurl;
  private edittext downloadnum;
  private button downloadbutton;
  private progressbar downloadprogressbar;
  private textview downloadinfo;
  private int downloadedsize = 0;
  private int filesize = 0;
  private long downloadtime;
  @override
  public void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.threaddemo);
    downloadurl = (textview) findviewbyid(r.id.downloadurl);
    downloadurl.settext("http://file16.top100.cn/201105110911/aa5cc27cbe34deb50a194581d1300881/special_323149/%e8%8d%b7%e5%a1%98%e6%9c%88%e8%89%b2.mp3");
    downloadnum = (edittext) findviewbyid(r.id.downloadnum);
    downloadinfo = (textview) findviewbyid(r.id.downloadinfo);
    downloadbutton = (button) findviewbyid(r.id.downloadbutton);
    downloadprogressbar = (progressbar) findviewbyid(r.id.downloadprogressbar);
    downloadprogressbar.setvisibility(view.visible);
    downloadprogressbar.setmax(100);
    downloadprogressbar.setprogress(0);
    downloadbutton.setonclicklistener(new onclicklistener() {
      public void onclick(view v) {
        download();
        downloadtime = systemclock.currentthreadtimemillis();
      }
    });
  }
  private void download() {
    // 获取sd卡目录 
    string dowloaddir = environment.getexternalstoragedirectory()
        + "/threaddemodownload/";
    file file = new file(dowloaddir);
    //创建下载目录 
    if (!file.exists()) {
      file.mkdirs();
    }
    //读取下载线程数,如果为空,则单线程下载 
    int downloadtn = integer.valueof("".equals(downloadnum.gettext()
        .tostring()) ? "1" : downloadnum.gettext().tostring());
    string filename = "hetang.mp3";
    //开始下载前把下载按钮设置为不可用 
    downloadbutton.setclickable(false);
    //进度条设为0 
    downloadprogressbar.setprogress(0);
    //启动文件下载线程 
    new downloadtask("http://file16.top100.cn/201105110911/aa5cc27cbe34deb50a194581d1300881/special_323149/%e8%8d%b7%e5%a1%98%e6%9c%88%e8%89%b2.mp3", integer
        .valueof(downloadtn), dowloaddir + filename).start();
  }
  handler handler = new handler() {
    @override
    public void handlemessage(message msg) {
      //当收到更新视图消息时,计算已完成下载百分比,同时更新进度条信息 
      int progress = (double.valueof((downloadedsize * 1.0 / filesize * 100))).intvalue();
      if (progress == 100) {
        downloadbutton.setclickable(true);
        downloadinfo.settext("下载完成!");
        dialog mdialog = new alertdialog.builder(threaddownloaddemo.this)
          .settitle("提示信息")
          .setmessage("下载完成,总用时为:"+(systemclock.currentthreadtimemillis()-downloadtime)+"毫秒")
          .setnegativebutton("确定", new dialoginterface.onclicklistener(){
            @override
            public void onclick(dialoginterface dialog, int which) {
              dialog.dismiss();
            }
          })
          .create();
        mdialog.show();
      } else {
        downloadinfo.settext("当前进度:" + progress + "%");
      }
      downloadprogressbar.setprogress(progress);
    }
  };
  public class downloadtask extends thread {
    private int blocksize, downloadsizemore;
    private int threadnum = 5;
    string urlstr, threadno, filename;
    public downloadtask(string urlstr, int threadnum, string filename) {
      this.urlstr = urlstr;
      this.threadnum = threadnum;
      this.filename = filename;
    }
    @override
    public void run() {
      filedownloadthread[] fds = new filedownloadthread[threadnum];
      try {
        url url = new url(urlstr);
        urlconnection conn = url.openconnection();
        //防止返回-1 
        inputstream in = conn.getinputstream();
        //获取下载文件的总大小 
        filesize = conn.getcontentlength();
        log.i("bb", "======================filesize:"+filesize);
        //计算每个线程要下载的数据量 
        blocksize = filesize / threadnum;
        // 解决整除后百分比计算误差 
        downloadsizemore = (filesize % threadnum);
        file file = new file(filename);
        for (int i = 0; i < threadnum; i++) {
          log.i("bb", "======================i:"+i);
          //启动线程,分别下载自己需要下载的部分 
          filedownloadthread fdt = new filedownloadthread(url, file, i * blocksize, (i + 1) * blocksize - 1);
          fdt.setname("thread" + i);
          fdt.start();
          fds[i] = fdt;
        }
        boolean finished = false;
        while (!finished) {
          // 先把整除的余数搞定 
          downloadedsize = downloadsizemore;
          finished = true;
          for (int i = 0; i < fds.length; i++) {
            downloadedsize += fds[i].getdownloadsize();
            if (!fds[i].isfinished()) {
              finished = false;
            }
          }
          handler.sendemptymessage(0);
          //线程暂停一秒 
          sleep(1000);
        }
      } catch (exception e) {
        e.printstacktrace();
      }
    }
  }
}
public class threaddownloaddemo extends activity {
private textview downloadurl;
private edittext downloadnum;
private button downloadbutton;
private progressbar downloadprogressbar;
private textview downloadinfo;
private int downloadedsize = 0;
private int filesize = 0;
private long downloadtime;
@overridepublic void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.threaddemo);
downloadurl = (textview) findviewbyid(r.id.downloadurl);
downloadurl.settext("http://file16.top100.cn/201105110911/aa5cc27cbe34deb50a194581d1300881/special_323149/%e8%8d%b7%e5%a1%98%e6%9c%88%e8%89%b2.mp3");
downloadnum = (edittext) findviewbyid(r.id.downloadnum);
downloadinfo = (textview) findviewbyid(r.id.downloadinfo);
downloadbutton = (button) findviewbyid(r.id.downloadbutton);
downloadprogressbar = (progressbar) findviewbyid(r.id.downloadprogressbar);
downloadprogressbar.setvisibility(view.visible);
downloadprogressbar.setmax(100);
downloadprogressbar.setprogress(0);
downloadbutton.setonclicklistener(new onclicklistener() {
public void onclick(view v) {download();
downloadtime = systemclock.currentthreadtimemillis();
}
}
);
}
private void download() {// 获取sd卡目录
string dowloaddir = environment.getexternalstoragedirectory()+ "/threaddemodownload/";
file file = new file(dowloaddir);
//创建下载目录
if (!file.exists()) {file.mkdirs();
}//读取下载线程数,如果为空,则单线程下载
int downloadtn = integer.valueof("".equals(downloadnum.gettext().tostring()) ? "1" : downloadnum.gettext().tostring());
string filename = "hetang.mp3";
//开始下载前把下载按钮设置为不可用
downloadbutton.setclickable(false);
//进度条设为0
downloadprogressbar.setprogress(0);
//启动文件下载线程
new downloadtask("http://file16.top100.cn/201105110911/aa5cc27cbe34deb50a194581d1300881/special_323149/%e8%8d%b7%e5%a1%98%e6%9c%88%e8%89%b2.mp3", integer.valueof(downloadtn), dowloaddir + filename).start();
}
handler handler = new handler() {@overridepublic void handlemessage(message msg) {
//当收到更新视图消息时,计算已完成下载百分比,同时更新进度条信息
int progress = (double.valueof((downloadedsize * 1.0 / filesize * 100))).intvalue();
if (progress == 100) {downloadbutton.setclickable(true);
downloadinfo.settext("下载完成!");
dialog mdialog = new alertdialog.builder(threaddownloaddemo.this).settitle("提示信息").setmessage("下载完成,总用时为:"+(systemclock.currentthreadtimemillis()-downloadtime)+"毫秒").setnegativebutton("确定", new dialoginterface.onclicklistener(){@overridepublic void onclick(dialoginterface dialog, int which) {dialog.dismiss();
}}).create();
mdialog.show();
} else {
downloadinfo.settext("当前进度:" + progress + "%");
}
downloadprogressbar.setprogress(progress);
}
};
public class downloadtask extends thread {private int blocksize, downloadsizemore;
private int threadnum = 5;
string urlstr, threadno, filename;
public downloadtask(string urlstr, int threadnum, string filename) {
this.urlstr = urlstr;
this.threadnum = threadnum;
this.filename = filename;
}
@overridepublic void run() {
filedownloadthread[] fds = new filedownloadthread[threadnum];
try {url url = new url(urlstr);
urlconnection conn = url.openconnection();
//防止返回-1
inputstream in = conn.getinputstream();
//获取下载文件的总大小
filesize = conn.getcontentlength();
log.i("bb", "======================filesize:"+filesize);
//计算每个线程要下载的数据量
blocksize = filesize / threadnum;
// 解决整除后百分比计算误差
downloadsizemore = (filesize % threadnum);
file file = new file(filename);
for (int i = 0; i < threadnum; i++) {
log.i("bb", "======================i:"+i);
//启动线程,分别下载自己需要下载的部分
filedownloadthread fdt = new filedownloadthread(url, file, i * blocksize, (i + 1) * blocksize - 1);
fdt.setname("thread" + i);
fdt.start();
fds[i] = fdt;
}
boolean finished = false;
while (!finished) {
// 先把整除的余数搞定
downloadedsize = downloadsizemore;
finished = true;
for (int i = 0; i < fds.length; i++) {
downloadedsize += fds[i].getdownloadsize();
if (!fds[i].isfinished()) {
finished = false;
}
}
handler.sendemptymessage(0);
//线程暂停一秒sleep(1000);
}
}
catch (exception e) {
e.printstacktrace();
}
}
}
}

这里启动线程将文件分割为几个部分,每一个部分再启动一个线程去下载数据
下载文件的线程

?
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
public class filedownloadthread extends thread{
  private static final int buffer_size=1024;
  private url url;
  private file file;
  private int startposition;
  private int endposition;
  private int curposition;
  //标识当前线程是否下载完成 
  private boolean finished=false;
  private int downloadsize=0;
  public filedownloadthread(url url,file file,int startposition,int endposition){
    this.url=url;
    this.file=file;
    this.startposition=startposition;
    this.curposition=startposition;
    this.endposition=endposition;
  }
  @override
  public void run() {
    bufferedinputstream bis = null;
    randomaccessfile fos = null;                        
    byte[] buf = new byte[buffer_size];
    urlconnection con = null;
    try {
      con = url.openconnection();
      con.setallowuserinteraction(true);
      //设置当前线程下载的起止点 
      con.setrequestproperty("range", "bytes=" + startposition + "-" + endposition);
      log.i("bb", thread.currentthread().getname()+" bytes=" + startposition + "-" + endposition);
      //使用java中的randomaccessfile 对文件进行随机读写操作 
      fos = new randomaccessfile(file, "rw");
      //设置写文件的起始位置 
      fos.seek(startposition);
      bis = new bufferedinputstream(con.getinputstream()); 
      //开始循环以流的形式读写文件 
      while (curposition < endposition) {
        int len = bis.read(buf, 0, buffer_size);        
        if (len == -1) {
          break;
        }
        fos.write(buf, 0, len);
        curposition = curposition + len;
        if (curposition > endposition) {
          downloadsize+=len - (curposition - endposition) + 1;
        } else {
          downloadsize+=len;
        }
      }
      //下载完成设为true 
      this.finished = true;
      bis.close();
      fos.close();
    } catch (ioexception e) {
      e.printstacktrace();
    }
  }
  public boolean isfinished(){
    return finished;
  }
  public int getdownloadsize() {
    return downloadsize;
  }
}
public class filedownloadthread extends thread{
private static final int buffer_size=1024;
private url url;
private file file;
private int startposition;
private int endposition;
private int curposition;//标识当前线程是否下载完成
private boolean finished=false;
private int downloadsize=0;
public filedownloadthread(url url,file file,int startposition,int endposition){
this.url=url;
this.file=file;
this.startposition=startposition;
this.curposition=startposition;
this.endposition=endposition;
}
@overridepublic void run() {
bufferedinputstream bis = null;
randomaccessfile fos = null;
byte[] buf = new byte[buffer_size];
urlconnection con = null;
try {
con = url.openconnection();
con.setallowuserinteraction(true);
//设置当前线程下载的起止点
con.setrequestproperty("range", "bytes=" + startposition + "-" + endposition);
log.i("bb", thread.currentthread().getname()+" bytes=" + startposition + "-" + endposition);
//使用java中的randomaccessfile 对文件进行随机读写操作
fos = new randomaccessfile(file, "rw");
//设置写文件的起始位置
fos.seek(startposition);
bis = new bufferedinputstream(con.getinputstream());
//开始循环以流的形式读写文件
while (curposition < endposition) {
int len = bis.read(buf, 0, buffer_size);
if (len == -1) {
break;
}
fos.write(buf, 0, len);
curposition = curposition + len;
if (curposition > endposition) {
downloadsize+=len - (curposition - endposition) + 1;
} else {
downloadsize+=len;
}
}
//下载完成设为true
this.finished = true;
bis.close();
fos.close();
} catch (ioexception e) {
e.printstacktrace();
}
}
public boolean isfinished(){return finished;}
public int getdownloadsize() {return downloadsize;}
}

这里通过randomaccessfile 的seek方法定位到相应的位置 并实时记录下载量
当然这里需要联网和访问sd卡 所以要加上相应的权限

?
1
2
3
<uses-permission android:name="android.permission.internet" />
<uses-permission android:name="android.permission.write_external_storage"></uses-permission>
<uses-permission android:name="android.permission.internet" /><uses-permission android:name="android.permission.write_external_storage"></uses-permission>

这样就ok了 下面可以看看断点续传的问题了。有待测试~~

希望本文所述对大家的android程序设计有所帮助。