Newer
Older
VipVideo / app / src / main / java / me / bello / viptv / util / down / Mp4Downloader.java
package me.bello.viptv.util.down;

import android.content.Context;
import android.os.Build;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.atomic.AtomicBoolean;

import me.bello.viptv.util.NotifyTool;
import me.bello.viptv.util.StringTool;

public class Mp4Downloader {
    private Context mContext;
    private AtomicBoolean canceled; // 取消状态,如果有一个子线程出现异常,则取消整个下载任务
    private DownloadFile file; // 下载的文件对象
    private String storageLocation;
    private final int threadCount = 10; // 线程数量
    private long fileSize; // 文件大小
    private String title;
    private String url;
    private int notifyId;
    private long beginTime; // 开始时间
    private Logger logger;
    private NotifyTool notifyTool;

    public Mp4Downloader(Context context, String title, String url, int notifyId) {
        this.mContext = context;
        this.title = title;
        this.url = url;
        this.notifyId = notifyId;

        notifyTool = new NotifyTool(mContext, notifyId);
    }


    public void start() {

        this.canceled = new AtomicBoolean(false);
        this.storageLocation = mContext.getExternalCacheDir() + "/mp4/";

        File fd = new File(storageLocation);
        if (!fd.exists()) {
            fd.mkdirs();
        }

        storageLocation += title + ".mp4";

        logger = new Logger(storageLocation + ".log", url, threadCount);

        boolean reStart = false;
        File f = new File(storageLocation + ".log");
        if (f.exists() && f.length() > 0) {
            reStart = true;
        }

        if (reStart) {
            logger = new Logger(storageLocation + ".log");
            System.out.printf("* 继续上次下载进度[已下载:%.2fMB]:%s\n", logger.getWroteSize() / 1014.0 / 1024, url);
        } else {
            System.out.println("* 开始下载:" + url);
            notifyTool.sendNotify(title + " 开始下载", 0);
        }
        if (-1 == (this.fileSize = getFileSize())) {
            System.out.println("获取下载文件信息异常,下载中断");
            notifyTool.sendNotify(title + " 获取下载文件信息异常,下载中断", 0);
            return;
        }
        System.out.printf("* 文件大小:%.2fMB\n", fileSize / 1024.0 / 1024);

        this.beginTime = System.currentTimeMillis();
        try {
            this.file = new DownloadFile(storageLocation, fileSize, logger);
            if (reStart) {
                file.setWroteSize(logger.getWroteSize());
            }
            // 分配线程下载
            dispatcher(reStart);
            // 循环打印进度
            printDownloadProgress();
        } catch (IOException e) {
            System.err.println("x 创建文件失败[" + e.getMessage() + "]");
            notifyTool.sendNotify(title + " 创建文件失败", 0);
        }
    }


    /**
     * 分配器,决定每个线程下载哪个区间的数据
     */

    private void dispatcher(boolean reStart) {
        long blockSize = fileSize / threadCount; // 每个线程要下载的数据量
        long lowerBound = 0, upperBound = 0;
        long[][] bounds = null;
        int threadID = 0;
        if (reStart) {
            bounds = logger.getBounds();
        }
        for (int i = 0; i < threadCount; i++) {
            if (reStart) {
                threadID = (int) (bounds[i][0]);
                lowerBound = bounds[i][1];
                upperBound = bounds[i][2];
            } else {
                threadID = i;
                lowerBound = i * blockSize;
                // fileSize-1
                upperBound = (i == threadCount - 1) ? fileSize - 1 : lowerBound + blockSize;
            }
            new DownloadTask(url, lowerBound, upperBound, file, canceled, threadID).start();
        }
    }


    /**
     * 循环打印进度,直到下载完毕,或任务被取消
     */

    private void printDownloadProgress() {

        long downloadedSize = file.getWroteSize();
        int i = 0;
        long lastSize = 0; // 三秒前的下载量
        while (!canceled.get() && downloadedSize < fileSize) {
            if (i++ % 4 == 3) { // 每3秒打印一次
                String percent = String.format("%.2f", downloadedSize / (double) fileSize * 100);
//                System.out.printf("下载进度:%.2f%%, 已下载:%.2fMB,当前速度:%.2fMB/s\n",
//                        downloadedSize / (double)fileSize * 100 ,
//                        downloadedSize / 1024.0 / 1024,
//                        (downloadedSize - lastSize) / 1024.0 / 1024 / 3);
                lastSize = downloadedSize;
                i = 0;

                StringTool.saveDownInfo(mContext, title, url, percent, notifyId);

                notifyTool.sendNotify(title, new BigDecimal(percent).intValue());

            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ignore) {
            }
            downloadedSize = file.getWroteSize();
        }
        file.close();
        if (canceled.get()) {
            try {
                File f = new File(storageLocation);
                f.delete();
//                Files.delete(Paths.get(storageLocation));
            } catch (Exception ignore) {
            }
//            System.err.println("x 下载失败,任务已取消");
            notifyTool.sendNotify(title + " 下载失败,请检查网络", 0);
        } else {
            StringTool.saveDownInfo(mContext, title, url, "100", notifyId);

            String pro = "[" + title + "] 下载成功,本次用时" + (System.currentTimeMillis() - beginTime) / 1000 + "秒";


            notifyTool.sendNotify(title + " 下载完成", 100);
        }
    }


    /**
     * @return 要下载的文件的尺寸
     */

    private long getFileSize() {
        if (fileSize != 0) {
            return fileSize;
        }
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) new URL(url).openConnection();
            conn.setConnectTimeout(5000);
            conn.setRequestMethod("GET");
            conn.connect();
            System.out.println("* 连接服务器成功");
            notifyTool.sendNotify(title + " 连接服务器成功", 0);
        } catch (MalformedURLException e) {
            throw new RuntimeException("URL错误");
        } catch (IOException e) {
            System.err.println("x 连接服务器失败[" + e.getMessage() + "]");
            notifyTool.sendNotify(title + " 连接服务器失败", 0);
            return -1;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return conn.getContentLengthLong();
        } else {
            return conn.getContentLength();
        }
    }


}