android 多线程下载 断点续传

来源:网易云课堂Android极客班第八次作业练习

练习内容: 多线程 asyncTask handler

多线程下载的原理

首先获取到目标文件的大小,然后在磁盘上申请一块空间用于保存目标文件,接着把目标文件分割成n份,分别创建线程下载.

获取目标文件的大小

                    //使用目标文件的下载链接作为发起网络请求
                    mUrl = new URL("http://download.sj.qq.com/upload/connAssitantDownload/upload/MobileAssistant_1.apk");
                    //注意不是URLConnection, URLConnection是抽象类,HttpURLConnection是它的子类
                    HttpURLConnection urlConnection = (HttpURLConnection) mUrl.openConnection();
                    urlConnection.setRequestMethod("GET");
                    urlConnection.setConnectTimeout(5000);
                    
                    //获取目标文件的大小
                    mContentLength = urlConnection.getContentLength();

在磁盘上申请一块空间,用于保存目标文件,这里用到了RandomAccessFile类,该类的seek()方法能够非常方便的对文件进行指定位置的定位.

                    mFile = new File(Environment.getExternalStorageDirectory(), getFileName(params[0]));
                    if (mFile.exists()) {//如果文件已经存在,删除
                        mFile.delete();
                    }
                    RandomAccessFile randomFile = new RandomAccessFile(mFile, "rw");
                    //设置文件大小
                    randomFile.setLength(mContentLength);

分割文件,分别创建线程进行下载(只需要关注非注释内容)

                    int blockSize = mContentLength / 3;
                    for (int i = 0; i < 3; i++) {
                        int begin = i * blockSize;
                        int end = (i + 1) * blockSize - 1;
                        if (i == 2) {
                            end = mContentLength;
                        }
                        //HashMap<String, Integer> map = new HashMap<>();
                        //map.put("begin", begin);
                        //map.put("end", end);
                        //map.put("finished", 0);
                        //threadList.add(map);
                        
                        //new Thread
                        new Thread(new DownloadThread(i, begin, end, mFile, mUrl)).start();
                    }

断点续传的原理

在每个线程进行下载的过程中,每次写入文件的时候,记录已经下载了多少内容,重新开始下载时,从上次结束的位置继续下载.

数据结构

使用HashMap存储该线程下载文件的起始位置,结束位置和已完成大小,并使用一个ArrayList存储各线程数据

    private List<HashMap<String, Integer>> threadList = new ArrayList<>();
                        HashMap<String, Integer> map = new HashMap<>();
                        map.put("begin", begin);//开始位置
                        map.put("end", end);//结束位置
                        map.put("finished", 0);//已完成

发起网络请求时,指定起止位置

                HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
                urlConnection.setRequestMethod("GET");

                //指定起止位置
                urlConnection.setRequestProperty("Range", "bytes=" + begin + "-" + end);

下载时更新map中的finished字段的值

                while ((len = is.read(buf)) != -1 && isDownloading) {
                    randomFile.write(buf, 0, len);
                    updateProgress(len);

                    //更新map中的finished字段的值
                    map.put("finished", map.get("finished") + len);
                }

再次开始下载时,更新每个线程的开始位置(即实例化DownloadThread类时所需要的第二个参数)

                for (int i = 0; i < threadList.size(); i++) {
                    HashMap<String, Integer> map = threadList.get(i);
                    new Thread(new DownloadThread(i, map.get("begin") + map.get("finished"), map.get("end"), mFile, mUrl)).start();
                }

更新ui的操作

自定义handler

            public static class DownloadHandler extends android.os.Handler {
                public final WeakReference<MainActivity> mActivity;
        
                public DownloadHandler(MainActivity activity) {
                    mActivity = new WeakReference<MainActivity>(activity);
                }
        
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    MainActivity activity = mActivity.get();
        
                    switch (msg.what) {
                        case 0:
                            int progress = (int) msg.obj;
                            activity.getProgressBar().setProgress(progress);
                            if (progress == 100) {
        //                        Toast.makeText(activity, "下载成功",Toast.LENGTH_SHORT).show();
                                activity.getDownloadButton().setText("下载成功");
                            }
                    }
                }
            }
      

更新UI的方法

            //使用synchronized
            synchronized private void updateProgress(int len) {
                total += len;
                int temp = total * 100 / mContentLength;
                mDownloadHandler.obtainMessage(0, temp).sendToTarget();
            }

完整代码github地址:https://github.com/zhangbz/MultithreadingDownloadDemo

你可能感兴趣的:(android 多线程下载 断点续传)