美文网首页
利用线程池下载文件

利用线程池下载文件

作者: CreScert | 来源:发表于2019-09-26 09:34 被阅读0次

    利用的HttpURLConnection下载的文件。但是没有考虑到某个线程下载失败的结果,没有想到更好的解决办法。
    变量声明:

        private File outfile; // 写入的文件
        private ThreadPoolExecutor threadPoolExecutor; // 线程池
    
        public int mFinishNum = 0; // 线程完成的数量
        public int mThreadNum = 10; // 线程数量
        public int bufferSize = 500 * 1024; // 缓存区 大小
    
        public long mRealWriteSize = 0; // 真实写入的大小
        private int mFileSize; // 文件大小
    
        public int mCurrentPercent = 0; // 保存的进度
    

    核心线程的多少,最大线程数,存活时间,在AsyncTask中设置的:

        // 这个是获取CPU的核数
        private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
        // We want at least 2 threads and at most 4 threads in the core pool,
        // preferring to have 1 less than the CPU count to avoid saturating
        // the CPU with background work
        private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
        private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
        private static final int KEEP_ALIVE_SECONDS = 30;
    

    1.首先通过getContentLength获取文件大小;

        /**
         * 获取文件大小
         */
        public void getFileSize() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    URL url;
                    HttpURLConnection httpURLConnection = null;
                    try {
                        url = new URL(urlpath);
                        httpURLConnection = (HttpURLConnection) url.openConnection();
                        httpURLConnection.setConnectTimeout(5 * 1000);
                        httpURLConnection.setReadTimeout(5 * 1000);
                        httpURLConnection.setRequestMethod("GET");
                        httpURLConnection.setRequestProperty("Charset", "UTF-8");
                        int responseCode = httpURLConnection.getResponseCode();
                        if (responseCode == 200) {
                            // 文件的大小
                            mFileSize = httpURLConnection.getContentLength();
                            if (mFileSize == 0) {
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        Toast.makeText(PdfShowActivity.this, "下载失败,代码" + responseCode, Toast.LENGTH_SHORT).show();
                                    }
                                });
                                return;
                            }
                            // 位置
                            outfile = new File(outfilepath + "/", pdfNameAll);
                            // 设置文件后,开始分配任务并下载
                            RandomAccessFile raf = new RandomAccessFile(outfile, "rw");
                            raf.setLength(mFileSize);
                            raf.close();
                            // 开始下载
                            startDown(mFileSize);
                        } else {
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    Toast.makeText(PdfShowActivity.this, "下载失败,代码" + responseCode, Toast.LENGTH_SHORT).show();
                                }
                            });
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    httpURLConnection.disconnect();
         }).start();
    }
    
    

    获取到大小之后,就可以创建线程池给每个线程分配任务开始下载了

        /**
         * 开始下载
         */
        private void startDown(int fileSize) {
            Log.e("文件大小", fileSize + "");
            int threadNum = mThreadNum; // 线程的数量
            // 就使用最明了的线程池下载,此处的核心线程数和最大线程数以及存活空闲时间可以参看最上边的AysncTask的
            threadPoolExecutor = new ThreadPoolExecutor(threadNum / 3, threadNum / 2, 30L, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(100));
            // 分段,如果正好是线程数量的倍数,就直接赋值,否则在末尾还有
            int block = fileSize % threadNum == 0 ? fileSize / threadNum : fileSize / threadNum + 1;
    
            // 循环给每个线程分配任务
            for (int i = 0; i < threadNum; i++) {
                // 开始下载的地方
                int downStart = block * i;
                // 结束下载的地方
                int downEnd = downStart + block >= fileSize ? fileSize : downStart + block - 1;
                ThreadTask threadTask = new ThreadTask(downStart, downEnd);
                threadPoolExecutor.execute(threadTask);
    
            }
        }
    

    这里单独写一个类是因为好多变量自己管理,

    public class ThreadTask implements Runnable {
            private final int mStartPart; // 线程开始的部分
            private final int mEndPart; // 线程结束的部分
            public boolean isFinish; // 是否结束
    
            ThreadTask(int startPart, int endPart) {
                this.mStartPart = startPart;
                mEndPart = endPart;
            }
    
            @Override
            public void run() {
                URL url;
                HttpURLConnection httpURLConnection;
                try {
                    url = new URL(urlpath);
                    httpURLConnection = (HttpURLConnection) url.openConnection();
                    httpURLConnection.setConnectTimeout(5 * 1000);
                    httpURLConnection.setReadTimeout(5 * 1000);
                    httpURLConnection.setRequestMethod("GET");
                    httpURLConnection.setRequestProperty("Charset", "UTF-8");
                    httpURLConnection.setRequestProperty("Range", "bytes=" + mStartPart + "-" + mEndPart);
                    int responseCode = httpURLConnection.getResponseCode();
                    httpURLConnection.connect();
                    // 此处成功为206,不是200
                    if (responseCode == 206) {
                        InputStream inputStream = httpURLConnection.getInputStream();
                        int len; // 写入长度
                        byte[] bytes = new byte[bufferSize];
    
                        RandomAccessFile rwd = new RandomAccessFile(outfile, "rwd");
                        rwd.seek(mStartPart);
    
                        // 开始写入文件
                        while ((len = inputStream.read(bytes)) != -1) {
                            rwd.write(bytes, 0, len);
                            // 设置进度
                            synchronized (PdfShowActivity.class) {
                                mRealWriteSize += len;
                                synchronized (MyApplication.class) {
                                    runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            setPercent();
                                        }
                                    });
                                }
                            }
                        }
                        rwd.close();
                        inputStream.close();
                        isFinish = true;
                        Log.e("线程下载完成", Thread.currentThread().getName() + "线程从" + mStartPart + "开始下载文件,结束于" + mEndPart);
                        synchronized (PdfShowActivity.class) {
                            mFinishNum++; // 这个是线程完成的数量,如果所有线程都完成了就说明文件下载完成了
                            synchronized (MyApplication.class) {
                                if (mFinishNum == mThreadNum) {
                                    Log.e("线程下载", "文件全部下载完成");
                                    runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            // dialog.dismiss();
                                        }
                                    });
                                    startPDF();
                                }
                            }
                        }
    
                    } else {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(PdfShowActivity.this, "打开文件失败", Toast.LENGTH_SHORT).show();
                            }
                        });
                        return;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
    

    如果需要,可以最后设置下进度

    /**
         * 设置进度
         */
        public void setPercent() {
            int percent = (int) (((float) mRealWriteSize) / mFileSize * 100);
            if (percent != mCurrentPercent) {
                mCurrentPercent = percent;
                // dialog.setMessage("加载进度: " + percent + "%");
            }
        }
    

    相关文章

      网友评论

          本文标题:利用线程池下载文件

          本文链接:https://www.haomeiwen.com/subject/ynhjuctx.html