Android进阶:实现多线程下载文件

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

 

首先看下效果图

 

Android进阶:实现多线程下载文件_第1张图片

 

创建工程 ThreadDemo

 

首先布局文件 threaddemo.xml

 

[c-sharp] view plaincopy
  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>  

 

主界面 Acitivity

 

[c-sharp] view plaincopy
  1. public class ThreadDownloadDemo extends Activity {  
  2.    
  3.     private TextView downloadurl;  
  4.     private EditText downloadnum;  
  5.     private Button downloadbutton;  
  6.     private ProgressBar downloadProgressBar;  
  7.     private TextView downloadinfo;  
  8.     private int downloadedSize = 0;  
  9.     private int fileSize = 0;  
  10.       
  11.     private long downloadtime;  
  12.    
  13.     @Override  
  14.     public void onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.         setContentView(R.layout.threaddemo);  
  17.    
  18.         downloadurl = (TextView) findViewById(R.id.downloadurl);  
  19.         downloadurl.setText("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3");  
  20.         downloadnum = (EditText) findViewById(R.id.downloadnum);  
  21.         downloadinfo = (TextView) findViewById(R.id.downloadinfo);  
  22.         downloadbutton = (Button) findViewById(R.id.downloadbutton);  
  23.         downloadProgressBar = (ProgressBar) findViewById(R.id.downloadProgressBar);  
  24.         downloadProgressBar.setVisibility(View.VISIBLE);  
  25.         downloadProgressBar.setMax(100);  
  26.         downloadProgressBar.setProgress(0);  
  27.         downloadbutton.setOnClickListener(new OnClickListener() {  
  28.             public void onClick(View v) {  
  29.                 download();  
  30.                 downloadtime = SystemClock.currentThreadTimeMillis();  
  31.             }  
  32.         });  
  33.     }  
  34.    
  35.     private void download() {  
  36.         // 获取SD卡目录  
  37.         String dowloadDir = Environment.getExternalStorageDirectory()  
  38.                 + "/threaddemodownload/";  
  39.         File file = new File(dowloadDir);  
  40.         //创建下载目录  
  41.         if (!file.exists()) {  
  42.             file.mkdirs();  
  43.         }  
  44.           
  45.         //读取下载线程数,如果为空,则单线程下载  
  46.         int downloadTN = Integer.valueOf("".equals(downloadnum.getText()  
  47.                 .toString()) ? "1" : downloadnum.getText().toString());  
  48.         String fileName = "hetang.mp3";  
  49.         //开始下载前把下载按钮设置为不可用  
  50.         downloadbutton.setClickable(false);  
  51.         //进度条设为0  
  52.         downloadProgressBar.setProgress(0);  
  53.         //启动文件下载线程  
  54.         new downloadTask("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3", Integer  
  55.                 .valueOf(downloadTN), dowloadDir + fileName).start();  
  56.     }  
  57.    
  58.     Handler handler = new Handler() {  
  59.         @Override  
  60.         public void handleMessage(Message msg) {  
  61.             //当收到更新视图消息时,计算已完成下载百分比,同时更新进度条信息  
  62.             int progress = (Double.valueOf((downloadedSize * 1.0 / fileSize * 100))).intValue();  
  63.             if (progress == 100) {  
  64.                 downloadbutton.setClickable(true);  
  65.                 downloadinfo.setText("下载完成!");  
  66.                 Dialog mdialog = new AlertDialog.Builder(ThreadDownloadDemo.this)  
  67.                     .setTitle("提示信息")  
  68.                     .setMessage("下载完成,总用时为:"+(SystemClock.currentThreadTimeMillis()-downloadtime)+"毫秒")  
  69.                     .setNegativeButton("确定"new DialogInterface.OnClickListener(){  
  70.                         @Override  
  71.                         public void onClick(DialogInterface dialog, int which) {  
  72.                             dialog.dismiss();  
  73.                         }  
  74.                     })  
  75.                     .create();  
  76.                 mdialog.show();  
  77.             } else {  
  78.                 downloadinfo.setText("当前进度:" + progress + "%");  
  79.             }  
  80.             downloadProgressBar.setProgress(progress);  
  81.         }  
  82.    
  83.     };  
  84.    
  85.       
  86.     public class downloadTask extends Thread {  
  87.         private int blockSize, downloadSizeMore;  
  88.         private int threadNum = 5;  
  89.         String urlStr, threadNo, fileName;  
  90.    
  91.         public downloadTask(String urlStr, int threadNum, String fileName) {  
  92.             this.urlStr = urlStr;  
  93.             this.threadNum = threadNum;  
  94.             this.fileName = fileName;  
  95.         }  
  96.    
  97.         @Override  
  98.         public void run() {  
  99.             FileDownloadThread[] fds = new FileDownloadThread[threadNum];  
  100.             try {  
  101.                 URL url = new URL(urlStr);  
  102.                 URLConnection conn = url.openConnection();  
  103.                 //防止返回-1  
  104.                 InputStream in = conn.getInputStream();  
  105.                 //获取下载文件的总大小  
  106.                 fileSize = conn.getContentLength();  
  107.                 Log.i("bb""======================fileSize:"+fileSize);  
  108.                 //计算每个线程要下载的数据量  
  109.                 blockSize = fileSize / threadNum;  
  110.                 // 解决整除后百分比计算误差  
  111.                 downloadSizeMore = (fileSize % threadNum);  
  112.                 File file = new File(fileName);  
  113.                 for (int i = 0; i < threadNum; i++) {  
  114.                     Log.i("bb""======================i:"+i);  
  115.                     //启动线程,分别下载自己需要下载的部分  
  116.                     FileDownloadThread fdt = new FileDownloadThread(url, file, i * blockSize, (i + 1) * blockSize - 1);  
  117.                     fdt.setName("Thread" + i);  
  118.                     fdt.start();  
  119.                     fds[i] = fdt;  
  120.                 }  
  121.                 boolean finished = false;  
  122.                 while (!finished) {  
  123.                     // 先把整除的余数搞定  
  124.                     downloadedSize = downloadSizeMore;  
  125.                     finished = true;  
  126.                     for (int i = 0; i < fds.length; i++) {  
  127.                         downloadedSize += fds[i].getDownloadSize();  
  128.                         if (!fds[i].isFinished()) {  
  129.                             finished = false;  
  130.                         }  
  131.                     }  
  132.                     handler.sendEmptyMessage(0);  
  133.                     //线程暂停一秒  
  134.                     sleep(1000);  
  135.                 }  
  136.             } catch (Exception e) {  
  137.                 e.printStackTrace();  
  138.             }  
  139.    
  140.         }  
  141.     }  
  142. }  

 

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

 

下载文件的线程

 

[c-sharp] view plaincopy
  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.    
  58.     public boolean isFinished(){  
  59.         return finished;  
  60.     }  
  61.    
  62.     public int getDownloadSize() {  
  63.         return downloadSize;  
  64.     }  
  65. }  

 

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

 

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

 

[c-sharp]  view plain copy
  1. <uses-permission android:name="android.permission.INTERNET" />  
  2.     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>  

你可能感兴趣的:(Android进阶:实现多线程下载文件)