首页 > 系统 > Android > 正文

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

2019-10-24 20:35:41
字体:大 中 小
来源:转载
供稿:网友

这篇文章主要介绍了Android实现多线程下载文件的方法,以实例形式较为详细的分析了Android多线程文件传输及合并等操作的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下

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

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

首先看下效果图:

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

创建工程 ThreadDemo

首先布局文件 threaddemo.xml

 

 
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3. android:orientation="vertical" 
  4. android:layout_width="fill_parent" 
  5. android:layout_height="fill_parent" 
  6. >  
  7. <TextView 
  8. android:layout_width="fill_parent" 
  9. android:layout_height="wrap_content" 
  10. android:text="下载地址" 
  11. />  
  12. <TextView 
  13. android:id="@+id/downloadurl" 
  14. android:layout_width="fill_parent" 
  15. android:layout_height="wrap_content" 
  16. android:lines="5" 
  17. />  
  18. <TextView 
  19. android:layout_width="fill_parent" 
  20. android:layout_height="wrap_content" 
  21. android:text="线程数" 
  22. />  
  23. <EditText 
  24. android:id="@+id/downloadnum" 
  25. android:layout_width="fill_parent" 
  26. android:layout_height="wrap_content" 
  27. />  
  28. <ProgressBar 
  29. android:id="@+id/downloadProgressBar" 
  30. android:layout_width="fill_parent" 
  31. style="?android:attr/progressBarStyleHorizontal" 
  32. android:layout_height="wrap_content" 
  33. />  
  34. <TextView 
  35. android:id="@+id/downloadinfo" 
  36. android:layout_width="fill_parent" 
  37. android:layout_height="wrap_content" 
  38. android:text="下载进度 0" 
  39. />  
  40. <Button 
  41. android:id="@+id/downloadbutton" 
  42. android:layout_width="wrap_content" 
  43. android:layout_height="wrap_content" 
  44. android:text="开始下载" 
  45. />  
  46. </LinearLayout> 

 

 
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > 
  3. <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="下载地址" /> 
  4. <TextViewandroid:id="@+id/downloadurl"android:layout_width="fill_parent" android:layout_height="wrap_content" android:lines="5"/> 
  5. <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="线程数" /> 
  6. <EditTextandroid:id="@+id/downloadnum"android:layout_width="fill_parent" android:layout_height="wrap_content" /> 
  7. <ProgressBarandroid:id="@+id/downloadProgressBar"android:layout_width="fill_parent" style="?android:attr/progressBarStyleHorizontal" android:layout_height="wrap_content" /> 
  8. <TextViewandroid:id="@+id/downloadinfo"android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="下载进度 0"/> 
  9. <Buttonandroid:id="@+id/downloadbutton"android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="开始下载"/> 
  10. </LinearLayout>  

主界面 Acitivity

 

 
  1. public class ThreadDownloadDemo extends Activity {  
  2. private TextView downloadurl;  
  3. private EditText downloadnum;  
  4. private Button downloadbutton;  
  5. private ProgressBar downloadProgressBar;  
  6. private TextView downloadinfo;  
  7. private int downloadedSize = 0;  
  8. private int fileSize = 0;  
  9. private long downloadtime;  
  10. @Override 
  11. public void onCreate(Bundle savedInstanceState) {  
  12. super.onCreate(savedInstanceState);  
  13. setContentView(R.layout.threaddemo);  
  14. downloadurl = (TextView) findViewById(R.id.downloadurl);  
  15. downloadurl.setText("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3");  
  16. downloadnum = (EditText) findViewById(R.id.downloadnum);  
  17. downloadinfo = (TextView) findViewById(R.id.downloadinfo);  
  18. downloadbutton = (Button) findViewById(R.id.downloadbutton);  
  19. downloadProgressBar = (ProgressBar) findViewById(R.id.downloadProgressBar);  
  20. downloadProgressBar.setVisibility(View.VISIBLE);  
  21. downloadProgressBar.setMax(100);  
  22. downloadProgressBar.setProgress(0);  
  23. downloadbutton.setOnClickListener(new OnClickListener() {  
  24. public void onClick(View v) {  
  25. download();  
  26. downloadtime = SystemClock.currentThreadTimeMillis();  
  27. }  
  28. });  
  29. }  
  30. private void download() {  
  31. // 获取SD卡目录  
  32. String dowloadDir = Environment.getExternalStorageDirectory()  
  33. + "/threaddemodownload/";  
  34. File file = new File(dowloadDir);  
  35. //创建下载目录  
  36. if (!file.exists()) {  
  37. file.mkdirs();  
  38. }  
  39. //读取下载线程数,如果为空,则单线程下载  
  40. int downloadTN = Integer.valueOf("".equals(downloadnum.getText()  
  41. .toString()) ? "1" : downloadnum.getText().toString());  
  42. String fileName = "hetang.mp3";  
  43. //开始下载前把下载按钮设置为不可用  
  44. downloadbutton.setClickable(false);  
  45. //进度条设为0  
  46. downloadProgressBar.setProgress(0);  
  47. //启动文件下载线程  
  48. new downloadTask("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3", Integer  
  49. .valueOf(downloadTN), dowloadDir + fileName).start();  
  50. }  
  51. Handler handler = new Handler() {  
  52. @Override 
  53. public void handleMessage(Message msg) {  
  54. //当收到更新视图消息时,计算已完成下载百分比,同时更新进度条信息  
  55. int progress = (Double.valueOf((downloadedSize * 1.0 / fileSize * 100))).intValue();  
  56. if (progress == 100) {  
  57. downloadbutton.setClickable(true);  
  58. downloadinfo.setText("下载完成!");  
  59. Dialog mdialog = new AlertDialog.Builder(ThreadDownloadDemo.this)  
  60. .setTitle("提示信息")  
  61. .setMessage("下载完成,总用时为:"+(SystemClock.currentThreadTimeMillis()-downloadtime)+"毫秒")  
  62. .setNegativeButton("确定", new DialogInterface.OnClickListener(){  
  63. @Override 
  64. public void onClick(DialogInterface dialog, int which) {  
  65. dialog.dismiss();  
  66. }  
  67. })  
  68. .create();  
  69. mdialog.show();  
  70. } else {  
  71. downloadinfo.setText("当前进度:" + progress + "%");  
  72. }  
  73. downloadProgressBar.setProgress(progress);  
  74. }  
  75. };  
  76. public class downloadTask extends Thread {  
  77. private int blockSize, downloadSizeMore;  
  78. private int threadNum = 5;  
  79. String urlStr, threadNo, fileName;  
  80. public downloadTask(String urlStr, int threadNum, String fileName) {  
  81. this.urlStr = urlStr;  
  82. this.threadNum = threadNum;  
  83. this.fileName = fileName;  
  84. }  
  85. @Override 
  86. public void run() {  
  87. FileDownloadThread[] fds = new FileDownloadThread[threadNum];  
  88. try {  
  89. URL url = new URL(urlStr);  
  90. URLConnection conn = url.openConnection();  
  91. //防止返回-1  
  92. InputStream in = conn.getInputStream();  
  93. //获取下载文件的总大小  
  94. fileSize = conn.getContentLength();  
  95. Log.i("bb", "======================fileSize:"+fileSize);  
  96. //计算每个线程要下载的数据量  
  97. blockSize = fileSize / threadNum;  
  98. // 解决整除后百分比计算误差  
  99. downloadSizeMore = (fileSize % threadNum);  
  100. File file = new File(fileName);  
  101. for (int i = 0; i < threadNum; i++) {  
  102. Log.i("bb", "======================i:"+i);  
  103. //启动线程,分别下载自己需要下载的部分  
  104. FileDownloadThread fdt = new FileDownloadThread(url, file, i * blockSize, (i + 1) * blockSize - 1);  
  105. fdt.setName("Thread" + i);  
  106. fdt.start();  
  107. fds[i] = fdt;  
  108. }  
  109. boolean finished = false;  
  110. while (!finished) {  
  111. // 先把整除的余数搞定  
  112. downloadedSize = downloadSizeMore;  
  113. finished = true;  
  114. for (int i = 0; i < fds.length; i++) {  
  115. downloadedSize += fds[i].getDownloadSize();  
  116. if (!fds[i].isFinished()) {  
  117. finished = false;  
  118. }  
  119. }  
  120. handler.sendEmptyMessage(0);  
  121. //线程暂停一秒  
  122. sleep(1000);  
  123. }  
  124. } catch (Exception e) {  
  125. e.printStackTrace();  
  126. }  
  127. }  
  128. }  
  129. }  
  130. public class ThreadDownloadDemo extends Activity { 
  131. private TextView downloadurl; 
  132. private EditText downloadnum; 
  133. private Button downloadbutton; 
  134. private ProgressBar downloadProgressBar; 
  135. private TextView downloadinfo; 
  136. private int downloadedSize = 0; 
  137. private int fileSize = 0; 
  138. private long downloadtime; 
  139. @Overridepublic void onCreate(Bundle savedInstanceState) { 
  140. super.onCreate(savedInstanceState); 
  141. setContentView(R.layout.threaddemo); 
  142. downloadurl = (TextView) findViewById(R.id.downloadurl); 
  143. downloadurl.setText("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3"); 
  144. downloadnum = (EditText) findViewById(R.id.downloadnum); 
  145. downloadinfo = (TextView) findViewById(R.id.downloadinfo); 
  146. downloadbutton = (Button) findViewById(R.id.downloadbutton); 
  147. downloadProgressBar = (ProgressBar) findViewById(R.id.downloadProgressBar); 
  148. downloadProgressBar.setVisibility(View.VISIBLE); 
  149. downloadProgressBar.setMax(100); 
  150. downloadProgressBar.setProgress(0); 
  151. downloadbutton.setOnClickListener(new OnClickListener() { 
  152. public void onClick(View v) {download(); 
  153. downloadtime = SystemClock.currentThreadTimeMillis(); 
  154. } 
  155. } 
  156. ); 
  157. } 
  158. private void download() {// 获取SD卡目录 
  159. String dowloadDir = Environment.getExternalStorageDirectory()+ "/threaddemodownload/"; 
  160. File file = new File(dowloadDir); 
  161. //创建下载目录 
  162. if (!file.exists()) {file.mkdirs(); 
  163. }//读取下载线程数,如果为空,则单线程下载 
  164. int downloadTN = Integer.valueOf("".equals(downloadnum.getText().toString()) ? "1" : downloadnum.getText().toString()); 
  165. String fileName = "hetang.mp3"; 
  166. //开始下载前把下载按钮设置为不可用 
  167. downloadbutton.setClickable(false); 
  168. //进度条设为0 
  169. downloadProgressBar.setProgress(0); 
  170. //启动文件下载线程 
  171. 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(); 
  172. } 
  173. Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) { 
  174. //当收到更新视图消息时,计算已完成下载百分比,同时更新进度条信息 
  175. int progress = (Double.valueOf((downloadedSize * 1.0 / fileSize * 100))).intValue(); 
  176. if (progress == 100) {downloadbutton.setClickable(true); 
  177. downloadinfo.setText("下载完成!"); 
  178. 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(); 
  179. }}).create(); 
  180. mdialog.show(); 
  181. } else { 
  182. downloadinfo.setText("当前进度:" + progress + "%"); 
  183. } 
  184. downloadProgressBar.setProgress(progress); 
  185. } 
  186. };  
  187. public class downloadTask extends Thread {private int blockSize, downloadSizeMore; 
  188. private int threadNum = 5; 
  189. String urlStr, threadNo, fileName; 
  190. public downloadTask(String urlStr, int threadNum, String fileName) { 
  191. this.urlStr = urlStr; 
  192. this.threadNum = threadNum; 
  193. this.fileName = fileName; 
  194. } 
  195. @Overridepublic void run() { 
  196. FileDownloadThread[] fds = new FileDownloadThread[threadNum]; 
  197. try {URL url = new URL(urlStr); 
  198. URLConnection conn = url.openConnection(); 
  199. //防止返回-1 
  200. InputStream in = conn.getInputStream(); 
  201. //获取下载文件的总大小 
  202. fileSize = conn.getContentLength(); 
  203. Log.i("bb", "======================fileSize:"+fileSize); 
  204. //计算每个线程要下载的数据量 
  205. blockSize = fileSize / threadNum; 
  206. // 解决整除后百分比计算误差 
  207. downloadSizeMore = (fileSize % threadNum); 
  208. File file = new File(fileName); 
  209. for (int i = 0; i < threadNum; i++) { 
  210. Log.i("bb", "======================i:"+i); 
  211. //启动线程,分别下载自己需要下载的部分 
  212. FileDownloadThread fdt = new FileDownloadThread(url, file, i * blockSize, (i + 1) * blockSize - 1); 
  213. fdt.setName("Thread" + i); 
  214. fdt.start(); 
  215. fds[i] = fdt; 
  216. } 
  217. boolean finished = false; 
  218. while (!finished) { 
  219. // 先把整除的余数搞定 
  220. downloadedSize = downloadSizeMore; 
  221. finished = true; 
  222. for (int i = 0; i < fds.length; i++) { 
  223. downloadedSize += fds[i].getDownloadSize(); 
  224. if (!fds[i].isFinished()) { 
  225. finished = false; 
  226. } 
  227. } 
  228. handler.sendEmptyMessage(0); 
  229. //线程暂停一秒sleep(1000); 
  230. } 
  231. } 
  232. catch (Exception e) { 
  233. e.printStackTrace(); 
  234. } 
  235. } 
  236. } 
  237. }  

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

下载文件的线程

 

 
  1. public class FileDownloadThread extends Thread{  
  2. private static final int BUFFER_SIZE=1024;  
  3. private URL url;  
  4. private File file;  
  5. private int startPosition;  
  6. private int endPosition;  
  7. private int curPosition;  
  8. //标识当前线程是否下载完成  
  9. private boolean finished=false;  
  10. private int downloadSize=0;  
  11. public FileDownloadThread(URL url,File file,int startPosition,int endPosition){  
  12. this.url=url;  
  13. this.file=file;  
  14. this.startPosition=startPosition;  
  15. this.curPosition=startPosition;  
  16. this.endPosition=endPosition;  
  17. }  
  18. @Override 
  19. public void run() {  
  20. BufferedInputStream bis = null;  
  21. RandomAccessFile fos = null;  
  22. byte[] buf = new byte[BUFFER_SIZE];  
  23. URLConnection con = null;  
  24. try {  
  25. con = url.openConnection();  
  26. con.setAllowUserInteraction(true);  
  27. //设置当前线程下载的起止点  
  28. con.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);  
  29. Log.i("bb", Thread.currentThread().getName()+" bytes=" + startPosition + "-" + endPosition);  
  30. //使用java中的RandomAccessFile 对文件进行随机读写操作  
  31. fos = new RandomAccessFile(file, "rw");  
  32. //设置写文件的起始位置  
  33. fos.seek(startPosition);  
  34. bis = new BufferedInputStream(con.getInputStream());  
  35. //开始循环以流的形式读写文件  
  36. while (curPosition < endPosition) {  
  37. int len = bis.read(buf, 0, BUFFER_SIZE);  
  38. if (len == -1) {  
  39. break;  
  40. }  
  41. fos.write(buf, 0, len);  
  42. curPosition = curPosition + len;  
  43. if (curPosition > endPosition) {  
  44. downloadSize+=len - (curPosition - endPosition) + 1;  
  45. } else {  
  46. downloadSize+=len;  
  47. }  
  48. }  
  49. //下载完成设为true  
  50. this.finished = true;  
  51. bis.close();  
  52. fos.close();  
  53. } catch (IOException e) {  
  54. e.printStackTrace();  
  55. }  
  56. }  
  57. public boolean isFinished(){  
  58. return finished;  
  59. }  
  60. public int getDownloadSize() {  
  61. return downloadSize;  
  62. }  
  63. }  
  64. public class FileDownloadThread extends Thread{ 
  65. private static final int BUFFER_SIZE=1024; 
  66. private URL url; 
  67. private File file; 
  68. private int startPosition; 
  69. private int endPosition; 
  70. private int curPosition;//标识当前线程是否下载完成 
  71. private boolean finished=false; 
  72. private int downloadSize=0; 
  73. public FileDownloadThread(URL url,File file,int startPosition,int endPosition){ 
  74. this.url=url; 
  75. this.file=file; 
  76. this.startPosition=startPosition; 
  77. this.curPosition=startPosition; 
  78. this.endPosition=endPosition; 
  79. } 
  80. @Overridepublic void run() { 
  81. BufferedInputStream bis = null; 
  82. RandomAccessFile fos = null; 
  83. byte[] buf = new byte[BUFFER_SIZE]; 
  84. URLConnection con = null; 
  85. try { 
  86. con = url.openConnection(); 
  87. con.setAllowUserInteraction(true); 
  88. //设置当前线程下载的起止点 
  89. con.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition); 
  90. Log.i("bb", Thread.currentThread().getName()+" bytes=" + startPosition + "-" + endPosition); 
  91. //使用java中的RandomAccessFile 对文件进行随机读写操作 
  92. fos = new RandomAccessFile(file, "rw"); 
  93. //设置写文件的起始位置 
  94. fos.seek(startPosition); 
  95. bis = new BufferedInputStream(con.getInputStream()); 
  96. //开始循环以流的形式读写文件 
  97. while (curPosition < endPosition) { 
  98. int len = bis.read(buf, 0, BUFFER_SIZE); 
  99. if (len == -1) { 
  100. break; 
  101. } 
  102. fos.write(buf, 0, len); 
  103. curPosition = curPosition + len; 
  104. if (curPosition > endPosition) { 
  105. downloadSize+=len - (curPosition - endPosition) + 1; 
  106. } else { 
  107. downloadSize+=len; 
  108. } 
  109. } 
  110. //下载完成设为true 
  111. this.finished = true; 
  112. bis.close(); 
  113. fos.close(); 
  114. } catch (IOException e) { 
  115. e.printStackTrace(); 
  116. } 
  117. } 
  118. public boolean isFinished(){return finished;} 
  119. public int getDownloadSize() {return downloadSize;} 
  120. } 

这里通过RandomAccessFile 的seek方法定位到相应的位置 并实时记录下载量

当然这里需要联网和访问SD卡 所以要加上相应的权限

 

  
  1. <uses-permission android:name="android.permission.INTERNET" />  
  2. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>  
  3. <uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> 

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

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表