編輯:關於android開發
多線程下載文件的過程是:
(1)首先獲得下載文件的長度,然後設置本地文件的長度。
HttpURLConnection.getContentLength();//獲取下載文件的長度
RandomAccessFile file = new RandomAccessFile("QQSetup.exe","rwd");
file.setLength(filesize);//設置本地文件的長度
(2)根據文件長度和線程數計算每條線程下載的數據長度和下載位置。
如:文件的長度為6M,線程數為3,那麼,每條線程下載的數據長度為2M,每條線程開始下載的位置如下圖所示。
例如10M大小,使用3個線程來下載,
線程下載的數據長度 (10%3 == 0 ? 10/3:10/3+1) ,第1,2個線程下載長度是4M,第三個線程下載長度為2M
下載開始位置:線程id*每條線程下載的數據長度 = ?
下載結束位置:(線程id+1)*每條線程下載的數據長度-1=?
(3)使用Http的Range頭字段指定每條線程從文件的什麼位置開始下載,下載到什麼位置為止,
如:指定從文件的2M位置開始下載,下載到位置(4M-1byte)為止
代碼如下:HttpURLConnection.setRequestProperty("Range", "bytes=2097152-4194303");
(4)保存文件,使用RandomAccessFile類指定每條線程從本地文件的什麼位置開始寫入數據。
RandomAccessFilethreadfile= newRandomAccessFile("QQWubiSetup.exe ","rwd");
threadfile.seek(2097152);//從文件的什麼位置開始寫入數據
/** *多線程下載,UI更新類 *@author young * */ public class MultiThreadDownload extends Thread{ private static final String TAG = "MultiThreadDownload"; /**每一個線程需要下載的大小 */ private int blockSize; /*** 線程數量 默認為5個線程下載*/ private int threadNum = 5; /*** 文件大小 */ private int fileSize; /** * 已經下載多少 */ private int downloadSize; /**文件的url,線程編號,文件名稱*/ private String UrlStr,ThreadNo,fileName; /***保存的路徑*/ private String savePath; /**下載的百分比*/ private int downloadPercent = 0; /**下載的 平均速度*/ private int downloadSpeed = 0; /**下載用的時間*/ private int usedTime = 0; /**當前時間*/ private long curTime; /**是否已經下載完成*/ private boolean completed = false; private Handler handler ; /** * 下載的構造函數 * @param url 請求下載的URL * @param handler UI更新使用 * @param savePath 保存文件的路徑 */ public MultiThreadDownload(Handler handler,String url,String savePath) { this.handler = handler; this.UrlStr = url; this.savePath = savePath; Log.e(TAG, toString()); } @Override public void run() { FileDownloadThread[] fds = new FileDownloadThread[threadNum];//設置線程數量 try { URL url = new URL(UrlStr); URLConnection conn = url.openConnection(); fileSize = conn.getContentLength(); this.fileName = FileUtil.getFileName(UrlStr); //只創建一個文件,saveFile下載內容 File saveFile = new File(savePath+"/"+fileName); Log.e(TAG, "文件一共:"+fileSize+" savePath "+savePath+" fileName "+fileName); RandomAccessFile accessFile = new RandomAccessFile(saveFile,"rwd"); //設置本地文件的長度和下載文件相同 accessFile.setLength(fileSize); accessFile.close(); //Handler更新UI,發送消息 sendMsg(FileUtil.startDownloadMeg); //每塊線程下載數據 blockSize = ((fileSize%threadNum)==0)?(fileSize/threadNum):(fileSize/threadNum+1); Log.e(TAG, "每個線程分別下載 :"+blockSize); for (int i = 0; i < threadNum; i++) { int curThreadEndPosition = (i+1)!=threadNum ? ((i+1)*blockSize-1) : fileSize; FileDownloadThread fdt = new FileDownloadThread(url, saveFile, i*blockSize, curThreadEndPosition); fdt.setName("thread"+i); fdt.start(); fds[i]=fdt; } /** * 獲取數據,更新UI,直到所有下載線程都下載完成。 */ boolean finished = false; //開始時間,放在循環外,求解的usedTime就是總時間 long startTime = System.currentTimeMillis(); while(!finished) { downloadSize = 0; finished = true; for (int i = 0; i < fds.length; i++) { downloadSize+= fds[i].getDownloadSize(); if(!fds[i].isFinished()) { finished = false; } } downloadPercent = (downloadSize*100)/fileSize; curTime = System.currentTimeMillis(); System.out.println("curTime = "+curTime+" downloadSize = "+downloadSize+" usedTime "+(int) ((curTime-startTime)/1000)); usedTime = (int) ((curTime-startTime)/1000); if(usedTime==0)usedTime = 1; downloadSpeed = (downloadSize/usedTime)/1024; sleep(1000);/*1秒鐘刷新一次界面*/ sendMsg(FileUtil.updateDownloadMeg); } Log.e(TAG, "下載完成"); completed = true; sendMsg(FileUtil.endDownloadMeg); } catch (Exception e) { Log.e(TAG, "multi file error Exception "+e.getMessage()); e.printStackTrace(); } super.run(); } /** * 得到文件的大小 * @return */ public int getFileSize() { return this.fileSize; } /** * 得到已經下載的數量 * @return */ public int getDownloadSize() { return this.downloadSize; } /** * 獲取下載百分比 * @return */ public int getDownloadPercent(){ return this.downloadPercent; } /** * 獲取下載速度 * @return */ public int getDownloadSpeed(){ return this.downloadSpeed; } /** * 修改默認線程數 * @param threadNum */ public void setThreadNum(int threadNum){ this.threadNum = threadNum; } /** * 分塊下載完成的標志 * @return */ public boolean isCompleted(){ return this.completed; } @Override public String toString() { return "MultiThreadDownload [threadNum=" + threadNum + ", fileSize=" + fileSize + ", UrlStr=" + UrlStr + ", ThreadNo=" + ThreadNo + ", savePath=" + savePath + "]"; } /** * 發送消息,用戶提示 * */ private void sendMsg(int what) { Message msg = new Message(); msg.what = what; handler.sendMessage(msg); }
public class FileDownloadThread extends Thread{ private static final String TAG = "FileDownloadThread"; /**緩沖區 */ private static final int BUFF_SIZE = 1024; /**需要下載的URL*/ private URL url; /**緩存的FIle*/ private File file; /**開始位置*/ private int startPosition; /**結束位置*/ private int endPosition; /**當前位置*/ private int curPosition; /**完成*/ private boolean finished = false; /**已經下載多少*/ private int downloadSize = 0; /*** * 分塊文件下載,可以創建多線程模式 * @param url 下載的URL * @param file 下載的文件 * @param startPosition 開始位置 * @param endPosition 結束位置 */ 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; Log.e(TAG, toString()); } @Override public void run() { BufferedInputStream bis = null; RandomAccessFile rAccessFile = null; byte[] buf = new byte[BUFF_SIZE]; URLConnection conn = null; try { conn = url.openConnection(); conn.setConnectTimeout(10000);//設置超時 conn.setReadTimeout(10000); conn.setAllowUserInteraction(true); System.out.println(this.getName()+" startPosition "+startPosition+" endPosition "+endPosition); conn.setRequestProperty("Range", "bytes="+(startPosition)+"-"+endPosition); //取剩余未下載的 rAccessFile = new RandomAccessFile(file,"rwd");//讀寫 //設置從什麼位置開始寫入數據 rAccessFile.seek(startPosition); bis = new BufferedInputStream(conn.getInputStream(), BUFF_SIZE); while(curPositionendPosition) { //如果下載多了,則減去多余部分 System.out.println(" curPosition > endPosition !!!!"); int extraLen = curPosition-endPosition; downloadSize += (len-extraLen+1); }else{ downloadSize+=len; } } this.finished = true; //當前階段下載完成 Log.e(TAG, "當前"+this.getName()+"下載完成"); } catch (Exception e) { Log.e(TAG, "download error Exception "+e.getMessage()); e.printStackTrace(); }finally{ //關閉流 FileUtil.closeInputStream(bis); try { rAccessFile.close(); } catch (IOException e) { // TODO Auto-generated catch block Log.e("AccessFile", "AccessFile IOException "+e.getMessage()); } } super.run(); } /** * 是否完成當前段下載完成 * @return */ public boolean isFinished() { return finished; } /** * 已經下載多少 * @return */ public int getDownloadSize() { return downloadSize; } @Override public String toString() { return "FileDownloadThread [url=" + url + ", file=" + file + ", startPosition=" + startPosition + ", endPosition=" + endPosition + ", curPosition=" + curPosition + ", finished=" + finished + ", downloadSize=" + downloadSize + "]"; }
示例源碼:demo下載
PostgreSQL空值null參與運算的處理方法 在數字加減乘除運算中遇到某個字段值為空值(null)的時候,輸出的結果往往會讓我們失望,得不到我們所期待的數值,可以通
Android 友盟分享詳細集成過程及所遇問題解決,android最近項目需要針對微信、朋友圈、QQ、QQ空間集成友盟分享的功能,說實話,我也是第一次做,期間碰到過很多問
《Android源碼設計模式解析與實戰》讀書筆記(二十一) 第二十一章、裝飾模式 裝飾模式也稱為包裝模式,是結構型設計模式之一。裝飾模式是一種用於替代繼承技術的一種方
「視頻直播技術詳解」系列之六:現代播放器原理, 關於直播的技術文章不少,成體系的不多。我們將用七篇文章,更系統化地介紹當下大熱的視頻直播各環節的關鍵技術,幫助視頻直播創