Android实现多线程下载文件的方法
程序员文章站
2023-11-12 09:34:04
本文实例讲述了android实现多线程下载文件的方法。分享给大家供大家参考。具体如下:
多线程下载大概思路就是通过range 属性实现文件分段,然后用randomacce...
本文实例讲述了android实现多线程下载文件的方法。分享给大家供大家参考。具体如下:
多线程下载大概思路就是通过range 属性实现文件分段,然后用randomaccessfile 来读写文件,最终合并为一个文件
首先看下效果图:
创建工程 threaddemo
首先布局文件 threaddemo.xml
<?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>
<?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
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(); } } } }
这里启动线程将文件分割为几个部分,每一个部分再启动一个线程去下载数据
下载文件的线程
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卡 所以要加上相应的权限
<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程序设计有所帮助。