1. 程式人生 > >Android核心技術-day05-05-安卓下的多執行緒下載(帶ProgressBar)有bug版

Android核心技術-day05-05-安卓下的多執行緒下載(帶ProgressBar)有bug版

package com.gaozewen.multidownloader;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;

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

public class MainActivity extends AppCompatActivity {

    private EditText mEt_thread_count;
    private EditText mEt_path;
    private LinearLayout mLl_container;

    public String path = "http://192.168.1.102:8080/xunlei.exe";
    public int totalThreadCount = 0;
    public int runningThreadCount = 0;
    private ArrayList<ProgressBar> mPbs;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 1. 找到控制元件
        // 2. 在 button 點選事件中下載檔案
        // 3. 下載的時候顯示下載進度
        mEt_thread_count = (EditText) findViewById(R.id.et_thread_count);
        mEt_path = (EditText) findViewById(R.id.et_path);
        mLl_container = (LinearLayout) findViewById(R.id.ll_container);
    }

    /**
     * 用第三方控制元件 xUtils 下載
     * @param view
     */
    public void download2(View view) {
        path =  mEt_path.getText().toString().trim();
        HttpUtils http = new HttpUtils();
        http.download(path, getCacheDir() + "/" + getDownloadFileName(path), true, new RequestCallBack<File>() {
            @Override
            public void onSuccess(ResponseInfo<File> responseInfo) {
                Toast.makeText(MainActivity.this,"下載成功", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFailure(HttpException e, String s) {
                Toast.makeText(MainActivity.this,"下載失敗", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onLoading(long total, long current, boolean isUploading) {
                System.out.println(current);
                super.onLoading(total, current, isUploading);
            }
        });
    }

    public void download(View view) {
        totalThreadCount = Integer.valueOf(mEt_thread_count.getText().toString().trim());
        path = mEt_path.getText().toString().trim();
        mLl_container.removeAllViews();

        mPbs = new ArrayList<>();
        for (int i = 0; i < totalThreadCount; i++) {
            // 有幾個執行緒就建立幾個 進度條
            ProgressBar pb = (ProgressBar) View.inflate(this, R.layout.pb, null);
            mLl_container.addView(pb);
            mPbs.add(pb);
        }

        new Thread() {
            @Override
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        int length = conn.getContentLength();
                        System.out.println("file length: " + length);
                        // 建立一個空的檔案,並且設定他的檔案長度等於伺服器上的檔案長度
                        RandomAccessFile raf = new RandomAccessFile(getCacheDir() + "/" + getDownloadFileName(path), "rw");
                        raf.setLength(length);
                        raf.close();

                        int blockSize = length / totalThreadCount;
                        System.out.println("every block size: " + blockSize);
                        runningThreadCount = totalThreadCount;
                        for (int threadId = 0; threadId < totalThreadCount; threadId++) {
                            int startPosition = blockSize * threadId;
                            int endPosition = blockSize * (threadId + 1) - 1; // -1 是到當前塊的最後一個位元組
                            if (threadId == (totalThreadCount - 1)) {
                                endPosition = length - 1;
                            }
                            System.out.println(String.format("threadId: %s\ndownload rage: %s to %s", threadId, startPosition, endPosition));

                            new DownloadThread(threadId, startPosition, endPosition).start();
                        }
                    } else {

                    }

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

    /**
     * 從網路路徑獲取檔名
     *
     * @param path 網路路徑
     * @return 檔名
     */
    private static String getDownloadFileName(String path) {
        return path.substring(path.lastIndexOf("/") + 1);
    }

    /**
     * 下載檔案的執行緒
     */
    private class DownloadThread extends Thread {
        /**
         * 執行緒 id
         */
        private int threadId;
        /**
         * 當前執行緒下載的起始位置
         */
        private int startPosition;
        /**
         * 當前執行緒下載的終止位置
         */
        private int endPosition;

        /**
         * 當前執行緒需要去下載的總共的位元組
         */
        private int threadTotal;

        /**
         * 上次斷點 下載的總共位元組數
         */
        private int lastDownloadTotalSize;

        public DownloadThread(int threadId, int startPosition, int endPosition) {
            this.threadId = threadId;
            this.startPosition = startPosition;
            this.endPosition = endPosition;
            this.threadTotal = endPosition - startPosition;

            mPbs.get(threadId).setMax(threadTotal); // 設定進度條
        }

        @Override
        public void run() {
            System.out.println(String.format("threadID: %s  begin working", threadId));
            // lest thread download it's self range data
            try {
                // 查詢 記錄斷點資訊的檔案
                File finfo = new File(getCacheDir(), totalThreadCount + getDownloadFileName(path) + threadId + ".txt");
                if (finfo.exists() && finfo.length() > 0) { // 斷點下載
                    FileInputStream fis = new FileInputStream(finfo);
                    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                    String lastPosition = br.readLine();
                    int intLastPosition = Integer.parseInt(lastPosition);

                    // 進度條斷點續借位置
                    lastDownloadTotalSize = intLastPosition - startPosition;

                    // This thread download data before times;
                    startPosition = intLastPosition;
                    fis.close();
                }
                // 第一次下載
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                System.out.println(String.format("begin and end: %s  range of download: %s to %s", threadId, startPosition, endPosition));
                conn.setRequestProperty("Range", String.format("bytes=%s-%s", startPosition, endPosition));
                // 從伺服器下載資源
                int code = conn.getResponseCode();
                if (code == 206) {
                    InputStream is = conn.getInputStream();
                    // 第一次沒有這個檔案則建立,下次直接開啟這個檔案
                    RandomAccessFile raf = new RandomAccessFile(getCacheDir() + "/" + getDownloadFileName(path), "rw");

                    // !!! position of begin to write
                    raf.seek(startPosition); // 將指標移動到這次開始續傳的位置

                    byte[] buffer = new byte[1024];
                    int len = -1;
                    int total = 0; // 當前執行緒 這次下載的資料
                    while ((len = is.read(buffer)) != -1) {
                        raf.write(buffer, 0, len);
                        total += len;
                        // 建立記錄 儲存當前執行緒下載的位置 (注意: rwd 同步實時寫入到裝置中)
                        RandomAccessFile infoRaf = new RandomAccessFile(getCacheDir() + "/" + totalThreadCount + getDownloadFileName(path) + threadId + ".txt", "rwd");
                        infoRaf.write(String.valueOf(startPosition + total).getBytes());
                        infoRaf.close();

                        // 每次讀寫顯示一個進度
                        mPbs.get(threadId).setProgress(total + lastDownloadTotalSize);
                    }
                    raf.close();
                    is.close();
                    System.out.println(String.format("thread: %s  download complete...", threadId));
                } else {
                    System.out.println("request download failed");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                synchronized (MainActivity.class) {
                    runningThreadCount--;
                    if (runningThreadCount <= 0) { // 多執行緒下載完成 刪除 位置資訊檔案
                        System.out.println("multi thread download complete.");
                        for (int i = 0; i < totalThreadCount; i++) {
                            File positionFile = new File(getCacheDir(),totalThreadCount + getDownloadFileName(path) + i + ".txt");
//                            System.out.println(positionFile.delete());
                        }
                    }
                }
            }
        }
    }

}