Android多線程斷點下載簡單實現

HTTP多線程斷點下載

原理:獲取目標文件的大小,在本地建立一個相同大小的文件,並計算每一個線程須要下載的起始位置及大小,而後分配至每一個線程獨立下載,所有下載完畢則自動合併.多線程

實現步驟

  1. 查看並計算目標文件的大小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);
  2. 設置目標文件在本地的映射ide

    RandomAccessFile raf = new RandomAccessFile(
            Environment.getExternalStorageDirectory().getAbsolutePath()
                    +"/"+getDownlaodName(mPath), "rw");
    raf.setLength(length);
  3. 開啓子線程並分配下載任務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();
  4. 子線程中的具體邏輯線程

    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

相關文章
相關標籤/搜索