原理:獲取目標文件的大小,在本地建立一個相同大小的文件,並計算每一個線程須要下載的起始位置及大小,而後分配至每一個線程獨立下載,所有下載完畢則自動合併.多線程
查看並計算目標文件的大小dom
URL url = new URL(mPath); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); int code = connection.getResponseCode(); if (code == 200) { int length = connection.getContentLength(); System.out.println("文件總長度爲:" + length);
設置目標文件在本地的映射ide
RandomAccessFile raf = new RandomAccessFile( Environment.getExternalStorageDirectory().getAbsolutePath() +"/"+getDownlaodName(mPath), "rw"); raf.setLength(length);
開啓子線程並分配下載任務url
int blokeSize = length / mTotalCount; System.out.println("每一塊大小爲:" + blokeSize); runningThreadCount = mTotalCount; for (int threadid = 0; threadid < mTotalCount; threadid++) { int startPosition = threadid * blokeSize; int endPosition = (threadid + 1) * blokeSize - 1; //最後一個線程應該下載至末尾 if (threadid == mTotalCount - 1) { endPosition = length - 1; } System.out.println("線程編號:" + threadid + "" + ",下載範圍:" + startPosition + "~~" + endPosition); //開啓下載子線程 new DownloadThread(threadid, startPosition, endPosition).start();
子線程中的具體邏輯線程
public void run() { System.out.println("線程"+threadid+"開始運行了"); try{ //讀存有歷史下載進度的文件,判斷是否已經下載過 File finfo=new File(Environment.getExternalStorageDirectory().getAbsolutePath() +"/"+mTotalCount + getDownlaodName(mPath)+threadid+".txt"); if (finfo.exists()&&finfo.length()>0) { //獲取文件輸入流 FileInputStream fis=new FileInputStream(finfo); //讀取緩衝 BufferedReader br=new BufferedReader(new InputStreamReader(fis)); //獲得文件內容 String lasrposition=br.readLine(); //轉化爲int值 int intlastposition=Integer.parseInt(lasrposition); lastLoadSize=intlastposition-startPosition; startPosition=intlastposition; fis.close(); } URL url=new URL(mPath); HttpURLConnection conn=(HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); System.out.println("線程實際下載:"+threadid+",範圍:"+startPosition +"~~"+endPosition); //設置http請求頭部參數 conn.setRequestProperty("Range", "bytes="+startPosition+"-"+endPosition); int code=conn.getResponseCode(); //206表明請求部分數據成功 if (code==206) { //拿到連接返回的輸入流 InputStream is=conn.getInputStream(); //指向要寫的文件(abc.exe) RandomAccessFile raf=new RandomAccessFile( Environment.getExternalStorageDirectory().getAbsolutePath() +"/"+getDownlaodName(mPath), "rw"); //指定文件開始寫的位置 raf.seek(startPosition); //下載緩衝區,越大下載越快,對硬盤損耗越小,可是越容易丟失數據 byte[] buffer=new byte[1024*1024]; int len=-1; int total=0;//當前線程本次下載數據總量 while ((len=is.read(buffer))!=-1) { raf.write(buffer,0,len); total+=len; //將每次更新的數據同步到底層硬盤 RandomAccessFile inforaf=new RandomAccessFile( Environment.getExternalStorageDirectory().getAbsolutePath() +"/"+mTotalCount +getDownlaodName(mPath)+threadid+".txt","rwd"); //保存當前線程下載到什麼位置 inforaf.write(String.valueOf(startPosition+total).getBytes()); inforaf.close(); } is.close(); raf.close(); System.out.println("線程"+threadid+"下載完畢"); } }catch(Exception e){ e.printStackTrace(); }finally{ //同步代碼塊,保證同時間僅有一個線程執行此區塊代碼 synchronized (MainActivity.class) { runningThreadCount--; if (runningThreadCount<=0) { System.out.println("多線程下載完畢"); for (int i = 0; i <mTotalCount ; i++) { File f=new File(Environment.getExternalStorageDirectory().getAbsolutePath() +"/"+mTotalCount +getDownlaodName(mPath)+i+".txt"); System.out.println(f.delete()); endTime=System.currentTimeMillis(); } System.out.println("結束時間:"+endTime); System.out.println("總共花費:"+(endTime-startTime)/1000+"秒"); runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), "總共花費:" +(endTime-startTime)/1000+"秒",Toast.LENGTH_SHORT).show(); } }); } } } }
備註:若是不使用斷點下載,只須要將判斷和存儲歷史下載信息的邏輯刪除便可。code