閱讀833 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Android進階:實現多線程下載文件

 

多線程下載大概思路就是通過Range 屬性實現文件分段,然後用RandomAccessFile 來讀寫文件,最終合並為一個文件

 

首先看下效果圖

 

 

創建工程 ThreadDemo

 

首先布局文件 threaddemo.xml

 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="https://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>  
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:andro 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: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:android:layout_width="fill_parent" android:layout_height="wrap_content" /><ProgressBarandroid:android:layout_width="fill_parent" android:layout_height="wrap_content" /><TextViewandroid:android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="下載進度 0"/><Buttonandroid:android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="開始下載"/></LinearLayout>

 

主界麵 Acitivity

 

  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("https://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("https://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. }  
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("https://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);//進度條設為0downloadProgressBar.setProgress(0);//啟動文件下載線程new downloadTask("https://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();//防止返回-1InputStream 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. 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. }  
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" />  
  1.     <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了 下麵可以看看斷點續傳的問題了。有待測試~~

最後更新:2017-04-02 06:51:45

  上一篇:go 關於java性能的小筆記
  下一篇:go Android中webview的使用