java网络编程之android多线程断点下载并提供下载进度(三)

来源:互联网 发布:js 代码格式化工具 编辑:程序博客网 时间:2024/05/16 05:36

转载请声明:http://blog.csdn.net/yoyo_newbie/article/details/49834419




如图,一个文件可以分n块,分别用一个线程去下载。只要知道某一个开始下载点,和某一点的下载结束点,就可以下载某一段下来。那么把所有下载好的段拼接后,就是完整的文件。这就是多线程下载文件的思路。为什要使用多线程系下载呢?在理想网络充足硬件理想好的情况下载,如果开了n线程去下载对比单线程下载,显然多线程的下载速度是单线程的 n倍快。

以下是本人封装好的demo ,本人用了线程池控制了线程并发,若想无限制在线活动线程数,自行修改。当然建议不改为好,毕竟设备支撑度始终有限

下载地址:https://github.com/Sam474850601/FileManagerDemo


视图:

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              xmlns:tools="http://schemas.android.com/tools"              android:layout_width="match_parent"              android:layout_height="match_parent"              android:padding="10dp"              android:orientation="vertical"        >    <TextView            android:id="@+id/tv_show"            android:text="点击下载"            android:layout_width="wrap_content"            android:layout_height="wrap_content"/>    <SeekBar            android:id="@+id/seekBar"            android:layout_marginTop="20dp"            android:layout_gravity="center_horizontal"            android:layout_width="match_parent" android:layout_height="wrap_content"/>    <ScrollView            android:layout_weight="1"            android:layout_width="match_parent"            android:layout_height="match_parent">        <TextView                android:id="@+id/state"                android:layout_width="match_parent"                android:layout_height="wrap_content"                />    </ScrollView>    <Button android:layout_width="match_parent" android:layout_height="wrap_content"            android:text="下载"            android:onClick="download"            /></LinearLayout>

使用方式:

public class MainActivity extends Activity {    //表述 下载前后情况    private TextView tv_show;    //下载文件进度条    private SeekBar seekBar;   //表述当前下载进度过程字节量    private TextView state;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tv_show = (TextView) findViewById(R.id.tv_show);        state = (TextView) findViewById(R.id.state);        seekBar = (SeekBar) findViewById(R.id.seekBar);    }    /**     * 下载点击按钮触发事件     * @param view 下载按钮     */    public void download(View view)    {        tv_show.setText("准备开始下载...");        NetManager netManager = new NetManager(this);        final File file = new File(Environment.getExternalStorageDirectory(), "/sam/mobileqq_android.apk");        //Q安卓安装包下载地址        String url = "http://sqdd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk";        //多线程下载文件,默认开4个线程。设置进程数可调用download( String url,   filePath,   threadNumber,   callback)        netManager.download( url, file,  new NetManager.DownloadCallback() {            @Override            public void downloading(long progress) {                seekBar.setProgress((int) progress); //设置进度条当前进度                state.append("当前下载量:"+progress+"\n");            }            @Override            public void onSuccess() {                tv_show.setText("文件下载成功!");            }            @Override            public void onPrepare(long filesize) {                state.setText("文件总大小:"+filesize+"\n");                tv_show.setText("开始下载...");                seekBar.setMax((int) filesize);//根据文件大小设置进度条的总大小            }            @Override            public void onFailure(int errorState, long fileSize, List<NetManager.FileMark> fileMarks) {                //如果有下载出现了下载量,且出现错误后可以保存fileMarks下来, 通过调用download(fileMarks, url, file,fiesiz, callback)继续下载。                //记录下载出错时候的原因                String error = null;                switch (errorState)                {                    case NetManager.DownloadCallback.ERROR_STATE_NOT_FOUN:                    {                        error = "下载地址不存在";                    }break;                    case NetManager.DownloadCallback.ERROR_STATE_SERVER_EXCEPTION:                    {                        error = "服务器配置异常无法访问";                    }break;                    case NetManager.DownloadCallback.ERROR_STATE_NETWORK_EXCEPTION:                    {                        error = "网络异常";                    }break;                    case NetManager.DownloadCallback.EROOR_STATE_FILE_MISS:                    {                        error = "文件丢失或无法创建文件";                    }break;                    case NetManager.DownloadCallback.ERROR_STATE_PROTOCOL_EXCEPTION:                    {                        error = "文件传输超出受限";                    }break;                    case NetManager.DownloadCallback.ERROR_STATE_OTHER:                    {                        error = "其他异常";                    }break;                }                tv_show.setText("下载失败!原因:"+error);            }        });    }} 

/** * 访问网络管理器 * @author Sam */public final class NetManager {    private Handler handler;    public NetManager( Context context) {        handler = new Handler(context.getMainLooper());    }    /**     * 多线程下载文件,默认开4个线程。     * @param url 文件下载地址     * @param filePath 文件保存的路径     *  @param callback  下载回调     */    public void download(String url, String filePath, DownloadCallback callback) {        download(url, filePath,4, callback);    }    /**     * 多线程下载文件,默认开4个线程。     * @param url 文件下载地址     * @param filePath 文件保存的路径     * @param callback  下载过程或结束回调     */    public void download(String url, File filePath, DownloadCallback callback) {        download(url, filePath.getAbsolutePath(), callback);    }    /**     * 多线程下载文件     * @param url          文件下载地址     * @param filePath      预设文件下载后的路径     * @param threadNumber  设置的执行的线程数目     * @param callback  下载过程或结束回调     */    public synchronized void download(final String url, final String filePath, final int threadNumber, final DownloadCallback callback) {        new Thread() {            @Override            public void run() {                InputStream inputStream = null;                RandomAccessFile randomAccessFile = null;                Integer errorState = -1;                long fileSize = 0;                try {                    HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();                    urlConnection.setRequestMethod("GET");                    urlConnection.setConnectTimeout(10 * 1000);                    urlConnection.setDoInput(true);                    File file = new File(filePath);                    if (!file.getParentFile().exists())                        file.getParentFile().mkdir();                    if (file.exists())                        file.delete();                    file.createNewFile();                    int responseCode = urlConnection.getResponseCode();                    if (responseCode >= 200 && responseCode < 300) {                        fileSize = urlConnection.getContentLength();//获取文件大小                        randomAccessFile = new RandomAccessFile(file, "rwd");                        randomAccessFile.setLength(fileSize);//设置文件大小                        randomAccessFile.close();//设置文件大小后关闭                        long value = fileSize % threadNumber;                        boolean isDivision = 0 == value;//是否整除                        long unit = isDivision ? fileSize / threadNumber : (fileSize - value) / threadNumber;                        int block = isDivision ? threadNumber : threadNumber + 1; //如果不整除,分多一块处理                        List<FileMark> fileMarks = new ArrayList<FileMark>();                        for (int i = 0; i < block; i++) {                            FileMark fileMark = new FileMark();                            fileMark.startMark = i * unit;//开始下载位置                            long enValue = (i + 1) * unit - 1;                            fileMark.endMark = i != block - 1?enValue:fileSize;                            fileMarks.add(fileMark);                        }                        download(fileMarks, url, file, fileSize, callback);                    } else {                        errorState = DownloadCallback.ERROR_STATE_SERVER_EXCEPTION;                    }                } catch (MalformedURLException e) {                    errorState = DownloadCallback.ERROR_STATE_NOT_FOUN;                } catch (FileNotFoundException e) {                    errorState = DownloadCallback.EROOR_STATE_FILE_MISS;                } catch (ProtocolException e) {                    errorState = DownloadCallback.ERROR_STATE_PROTOCOL_EXCEPTION;                } catch (IOException e) {                    errorState = DownloadCallback.ERROR_STATE_NETWORK_EXCEPTION;                } catch (Exception ex) {                    errorState = DownloadCallback.ERROR_STATE_OTHER;                } finally {                    if (null != randomAccessFile) {                        try {                            randomAccessFile.close();                        } catch (IOException e) {                            e.printStackTrace();                        }                    }                    if (null != inputStream) {                        try {                            inputStream.close();                        } catch (IOException e) {                            e.printStackTrace();                        }                    }                    if (-1 != errorState) {                        final int es = errorState;                        final long size = fileSize;                        handler.post(new Runnable() {                            @Override                            public void run() {                                callback.onFailure(es, size, null);                            }                        });                    }                }            }        }.start();    }    private class CloseThread {        int threadDistroyNum=1;        long downloadQuantity;        int errorState  = -1;        boolean hasException;    }    /**     * 多线程下载文件, 可以通过传入FileMark下载。     * @param fileMarks  需要下载文件段落集合     * @param url 下载路径     * @param file 下载文件     * @param fileSize 文件总长度     * @param callback 下载过程或结束回调     */    public synchronized void download(List<FileMark> fileMarks, String url, File file,                                      final long fileSize,  final DownloadCallback callback)    {        handler.post(new Runnable() {            @Override            public void run() {                callback.onPrepare(fileSize);            }        });        CloseThread ct = new CloseThread();        List<FileMark> remindFileMarks = new ArrayList<FileMark>();        long remindQuantity = 0;        for (FileMark fileMark : fileMarks) {            long value =  fileMark.endMark - fileMark.startMark+1;            remindQuantity += value;        }        ct.downloadQuantity = fileSize - remindQuantity+1;        for (FileMark fileMark : fileMarks) {            DownloadFileThread thread = new DownloadFileThread(url, file, fileMark, fileMarks.size(), ct,                    remindFileMarks, fileSize, callback);            ThreadManager.getPool().execute(thread);        }    }    /**     * 线程池管理器     */    public static  class ThreadManager {        /**         * 最大队列长度         */        private static final int MAX_QUEUE_LENGTH = 128;        /**         * 常驻内在线程数         */        private static final int ALIVE_THREAD_SIZE = 5;        /**         * 最大活动线程数         */        private static final int MAX_THREAD_SIZE = 15;        /**         * 线程空置多长时间销毁         */        private static final int THREAD_ALIVE_SECONDS = 60;        /**         * 线程池         */        private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(ALIVE_THREAD_SIZE,                MAX_THREAD_SIZE, THREAD_ALIVE_SECONDS, TimeUnit.SECONDS,                new ArrayBlockingQueue<Runnable>(MAX_QUEUE_LENGTH),                new ThreadPoolExecutor.DiscardOldestPolicy());        /**         * 取得线程池实例         *         * @return         */        public static final ThreadPoolExecutor getPool() {            return threadPool;        }    }    /**     * 下载执行回调     */    public interface DownloadCallback {        /**         * 下载地址不存在         */        public static final int ERROR_STATE_NOT_FOUN = 0x01;        /**         * 服务器配置异常无法访问         */        public static final int ERROR_STATE_SERVER_EXCEPTION = 0x02;        /**         * 网络异常         */        public static final int ERROR_STATE_NETWORK_EXCEPTION = 0x03;        /**         * 文件丢失或无法创建文件         */        public static final int EROOR_STATE_FILE_MISS = 0x04;        /**         *  文件传输超出受限         */        public static final int ERROR_STATE_PROTOCOL_EXCEPTION = 0x05;        /**         * 其他异常         */        public static final int ERROR_STATE_OTHER = 0x06;        /**         * 下载时候,进度情况         *         * @param progress  当前下载量         *         */        void downloading(long progress);        /**         * 下载成功         */        void onSuccess();        /**         * 下载之前回调         * @param filesize 文件长度         */        void onPrepare(long filesize);        /**         * 下载过程出现错误         *         * @param errorState  下载错误情况         * @param fileMarks  剩余为下载的部分         * @param fileSize   总文件大小         */        void onFailure(int errorState, long fileSize, List<FileMark> fileMarks);    }    /**     * 文件下载的位置信息     */    public static class FileMark {        /**         * 开始下载的长度         */        public  long startMark;        /**         *  终止下载的长度         */        public  long endMark;        @Override        public String toString() {            return "FileMark{" +                    "startMark=" + startMark +                    ", endMark=" + endMark +                    '}';        }    }    /**     * 下载文件块线程     */    private class DownloadFileThread extends Thread {        private String url;//下载地址        private File file;//文件路径        private FileMark fileMark;//文件下载块信息        private DownloadCallback callback;        private CloseThread ct;//线程结束数目        private int totalThread;//总线程数目        private long fileSize;        private List<FileMark> remindFileMarks;        public DownloadFileThread(String url, File file, FileMark fileMark, int totalThread, CloseThread ct,                                  List<FileMark> remindFileMarks, long fileSize, DownloadCallback callback) {            this.url = url;            this.file = file;            this.fileMark = fileMark;            this.totalThread = totalThread;            this.ct = ct;            this.callback = callback;            this.fileSize = fileSize;            this.remindFileMarks = remindFileMarks;        }        @Override        public void run() {            InputStream inputStream = null;            RandomAccessFile randomAccessFile = null;            long currentDownQuantity = 0;            try {                HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();                urlConnection.setRequestMethod("GET");                urlConnection.setConnectTimeout(10 * 1000);                urlConnection.setReadTimeout(10*1000);                urlConnection.setRequestProperty("Range", "bytes=" + fileMark.startMark + "-" + fileMark.endMark);                int responseCode = urlConnection.getResponseCode();                if (responseCode >= 200 && responseCode < 300) {                    inputStream = urlConnection.getInputStream();                    int len = 0;                    byte[] data = new byte[1024 * 8];                    randomAccessFile = new RandomAccessFile(file, "rwd");                    randomAccessFile.seek(fileMark.startMark);//设置往文件写入部分                    while (-1 != (len = inputStream.read(data))) {                        currentDownQuantity += len;                        randomAccessFile.write(data, 0, len);                        final long  value =(ct.downloadQuantity = ct.downloadQuantity + len);                        handler.post(new Runnable() {                            @Override                            public void run() {                                callback.downloading(value);                            }                        });                    }                } else {                    ct.errorState = DownloadCallback.ERROR_STATE_SERVER_EXCEPTION;                }            } catch (MalformedURLException e) {                ct.errorState  = DownloadCallback.ERROR_STATE_NOT_FOUN;            } catch (FileNotFoundException e) {                ct.errorState  = DownloadCallback.EROOR_STATE_FILE_MISS;            } catch (ProtocolException e) {                ct.errorState  = DownloadCallback.ERROR_STATE_PROTOCOL_EXCEPTION;            } catch (IOException e) {                ct.errorState  = DownloadCallback.ERROR_STATE_NETWORK_EXCEPTION;            } catch (Exception ex) {                ct.errorState  = DownloadCallback.ERROR_STATE_OTHER;            } finally {                if (null != randomAccessFile) {                    try {                        randomAccessFile.close();                    } catch (IOException e) {                    }                }                if (null != inputStream) {                    try {                        inputStream.close();                    } catch (IOException e) {                    }                }                if (-1 !=  ct.errorState )                    ct.hasException =true;                synchronized (ct)                {                    if( ct.threadDistroyNum++ == totalThread)                    {                        fileMark.startMark += currentDownQuantity;                        if(ct.hasException)                        {                            remindFileMarks.add(fileMark);                        }                        handler.post(new Runnable() {                            @Override                            public void run() {                                if (remindFileMarks.isEmpty()) {                                    callback.onSuccess();                                } else {                                    callback.onFailure( ct.errorState , fileSize, remindFileMarks);                                }                            }                        });                    }                }            }        }    }}
















0 0