android 多線程斷點續傳下載---強大的開源XUtils

XUtils

下面給大家介紹android的一個開源項目,它可以幫你幹好多事情,廢話不多說,步入正題: 
這是xUtil的下載地址,在GitHub 
目前xUtils主要的四大模塊:

DbUtils模塊:

        android中的orm框架,一行代碼就可以進行增刪改查;
        支持事務,默認關閉;
        可通過註解自定義表名,列名,外鍵,唯一性約束,NOT NULL約束,CHECK約束等(需要混淆的時候請註解表名和列名);
        支持綁定外鍵,保存實體時外鍵關聯實體自動保存或更新;
        自動加載外鍵關聯實體,支持延時加載;
        支持鏈式表達查詢,更直觀的查詢語義,參考下面的介紹或sample中的例子。

ViewUtils模塊:

        android中的ioc框架,完全註解方式就可以進行UI,資源和事件綁定;
        新的事件綁定方式,使用混淆工具混淆後仍可正常工作;
        目前支持常用的20種事件綁定,參見ViewCommonEventListener類和包com.lidroid.xutils.view.annotation.event。

HttpUtils模塊:

        支持同步,異步方式的請求;
        支持大文件上傳,上傳大文件不會oom;
        支持GET,POST,PUT,MOVE,COPY,DELETE,HEAD,OPTIONS,TRACE,CONNECT請求;
        下載支持301/302重定向,支持設置是否根據Content-Disposition重命名下載的文件;
        返回文本內容的請求(默認只啓用了GET請求)支持緩存,可設置默認過期時間和針對當前請求的過期時間。

BitmapUtils模塊:

        加載bitmap的時候無需考慮bitmap加載過程中出現的oom和android容器快速滑動時候出現的圖片錯位等現象;
        支持加載網絡圖片和本地圖片;
        內存管理使用lru算法,更好的管理bitmap內存;
        可配置線程加載線程數量,緩存大小,緩存路徑,加載顯示動畫等...

接下來,先給大家講解一下多線程下載的原理: 
如何實現多線程: 
1 如何等分服務器資源 (RandomAccessFile) 
2 如何在客戶端創建一個大小和服務器一模一樣的文件 
3 如何開啓多個線程 
4 如何知道每個線程都下載完畢

path = “http://localhost:9019/01.exe“; 這個網址是我tomcat的服務器,你可以在tomcat的apache-tomcat-7.0.42\webapps\ROOT 文件夾下放一個exe文件,下載它,運行tomcat服務器,localhost是你電腦的IP地址。

下面我用java寫一下多線程的原理:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

public class MutilDownload {
    private static String path = "http://localhost:9019/01.exe";
    private static int threadCount = 3;//線程的數量
    private static int runningThread;//當前正在運行的線程

    /**
     * @param args
     */
    @SuppressWarnings("resource")
    public static void main(String[] args) {
        // 獲取到服務器的資源
        try {
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);
            int code = conn.getResponseCode();
            if (code == 200) {
                // 1 獲取服務器資源的大小
                int length = conn.getContentLength();
                System.out.println("length:" + length);
                runningThread = threadCount;//當前正在運行的線程數
                // 2 要知道如何從客戶端創建一個大小和服務器一模一樣的文件
                RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rw");
                raf.setLength(length);//設置和服務器大小一樣的文件
                // 3 如何等分服務器的資源 length
                int threadBiockSize = length / threadCount;// 每個線程下載的大小
                for (int i = 0; i < threadCount; i++) {
                    int startIndex = i * threadBiockSize;// 每個線程下載開始的位置
                    int endIndex = (i + 1) * threadBiockSize - 1;// 每個線程下載的結束位置
                    // 最後一個線程
                    if (i == threadCount - 1) {
                        endIndex = length - 1;
                    }
                    System.out.println("線程id:"+i+"理論的下載位置--:"+startIndex+"-----"+endIndex);
                    // 開啓多個線程去下載
                    new DownloadThread(path, startIndex, endIndex, i).start();

                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 開啓多個線程下載
     * @author Blake
     *
     */
    private static class DownloadThread extends Thread {
        private String path;
        private int startIndex;
        private int endIndex;
        private int threadId;

        @SuppressWarnings("unused")
        public DownloadThread(String path, int startIndex, int endIndex,
                int threadId) {
            this.path = path;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.threadId = threadId;
        }


        @SuppressWarnings("resource")
        @Override
        public void run() {
            // 下載
            try {
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(5000);
                // 斷點續傳 判斷是否有下載的記錄
                File file = new File(threadId + ".txt");
                if (file.exists() && file.length() > 0) {
                    // 讀取上次保存的位置
                    FileInputStream fis = new FileInputStream(file);
                    BufferedReader bfr = new BufferedReader(
                            new InputStreamReader(fis));
                    String lastPositin = bfr.readLine();// 上一次下載的位置
                    // 按照上次的位置繼續下載
                    conn.setRequestProperty("Range", "bytes=" + lastPositin+ "-" + endIndex);
                    //要改變一下statindex 的位置
                    startIndex  = Integer.parseInt(lastPositin);
                    System.out.println("線程id:"+threadId+"真實的下載位置--:"+startIndex+"-----"+endIndex);
                    fis.close();

                } else {
                    // 因爲開啓多個線程去下載,要設置一個頭信息,告訴服務器每個線程每個線程下載的位置
                    conn.setRequestProperty("Range", "bytes=" + startIndex
                            + "-" + endIndex);
                }

                int code = conn.getResponseCode();
                if (code == 206) {// 返回服務器部分資源
                    InputStream in = conn.getInputStream();
                    // 把數據寫到文件中
                    RandomAccessFile raf = new RandomAccessFile(getFileName(path),"rw");
                    raf.seek(startIndex);
                    // 做斷點續傳 就是我把每個多線程下載的位置記錄起來
                    int len = -1;
                    int total = 0;
                    byte buffer[] = new byte[1024*1024];
                    while ((len = in.read(buffer)) != -1) {
                        raf.write(buffer, 0, len);
                        // 記錄當前線程下載的位置
                        total += len;// 當前線程下載的大小
                        int currentThreadPosition = startIndex + total;// 當前線程下載的位置
                                                                        // 把這個位置記錄下來
                        RandomAccessFile rAccessFile = new RandomAccessFile(
                                threadId + ".txt", "rwd");// 把數據同步到低層設備
                        rAccessFile.write(String.valueOf(currentThreadPosition)
                                .getBytes());
                        rAccessFile.close();
                    }
                    raf.close();
                    in.close();
                    System.out.println("線程-----下載完畢" + threadId);
                    //開一個鎖
                    synchronized (DownloadThread.class) {
                        runningThread--;
                        if (runningThread <=0) {
                            //說明所有的線程都下載完畢 我把.txt文件刪除
                            for (int i = 0; i < threadCount; i++) {
                                File deleteFile = new File(i+".txt");
                                deleteFile.delete();
                            }


                        }
                    }


                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            super.run();
        }
    }
    /**
     * 截取下載的路徑
     * @param path
     * @return
     */
    public static String getFileName(String path){
        int start=path.lastIndexOf("/")+1;
        return path.substring(start);

    }

}

這是多線程下載的核心代碼,看到這麼多代碼是不是很麻煩!!!!

接下我們用開源的項目來做就很簡單了 
1 首先把開源的com包導入項目中

HttpUtils http = new HttpUtils();
        /**
         * url 下載的路徑
         * target 下載文件保存的路徑
         * autoResume 是否支持斷點續傳
         */
        http.download("http://localhost:9019/01.exe", "/mnt/sdcard/haha.exe", true,new RequestCallBack<File>() {

            @Override
            public void onSuccess(ResponseInfo<File> responseInfo) {
                Toast.makeText(getApplicationContext(), responseInfo.result.getPath(), 1).show();

            }

            @Override
            public void onFailure(HttpException error, String msg) {


            }

            /**
             * 加載進度 pb爲進度條,實現進度條進度加載
             * total 總得大小
             * current 當前的大小
             */
            @Override
            public void onLoading(long total, long current, boolean isUploading) {

                pb.setMax((int) total);
                pb.setProgress((int) current);
            }
        });

怎麼樣就這麼幾行代碼,便實現了多線程斷點續傳下載和進度條加載進度。 
還有更多的功能自己去探索吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章