private void onDownloadError(int taskErrorCode) { int oldStatus = downloadFile.getStatus(); downloadFile.setStatus(DownloadFile.status_download_error); if (oldStatus != DownloadFile.status_download_error) { recorder.updateFile(context, downloadFile); notifyFileStatusChange(); } if (taskListener != null) { taskListener.onDownloadFailed(downloadFile, taskErrorCode); } }
/** * 管理一个下载任务 * * @author skg */ public class DownloadTask { private List<DownloadBlock> blocks = new ArrayList<DownloadBlock>(); private ExecutorService executor; private DownloadFile downloadFile; private TaskListener taskListener; private boolean isRunning = false; private TaskRunner currentRunner; private Context context; public DownloadTask(Context context, ExecutorService executor, DownloadFile df, TaskListener l) { this.context = context; this.executor = executor; downloadFile = df; taskListener = l; } public synchronized boolean start() { // 如果已经启动了,则直接返回,否则开启任务 if (isRunning) { return true; } if (executor == null || executor.isShutdown()) { return false; } isRunning = true; currentRunner = new TaskRunner(); Future<TaskRunner> f = executor.submit(currentRunner, currentRunner); currentRunner.boundFutrue(f); return true; } public synchronized boolean stop() { // 停止任务,并取消和线程的绑定,并移除block runner,再次启动的时候创建新的block runner if (!isRunning) { return true; } if (currentRunner != null) { currentRunner.stop(); } currentRunner = null; isRunning = false; return true; } /** * 判断是否正在运行 * * @return */ public synchronized boolean isRunning() { return isRunning; } private int blockTotalIncrease = 0; private BlockRunnerListener blockListener = new BlockRunnerListener() { @Override public void onBlockIncrease(BlockDownloader loader, DownloadBlock block, int increase) { // 块下载任务进度增加,累加当符合条件的时候同意向外通知进度 blockTotalIncrease += increase; } @Override public void onBlockLoadDone(BlockDownloader loader, DownloadBlock block) { // 块下载完成,通常情况下这个时候不需要做特殊处理 } @Override public void onBlockLoadFailed(BlockDownloader loader, DownloadBlock block, int errorCode) { // Block下载失败,判断原因看是否需要开启线程继续下载 // 1,如果是网络部可用,则停止全部下载,并通知外部监听 // 2,如果是连接超时则尝试重新加载 switch (errorCode) { case ERROR_CAN_NOT_FIND_OUT_FILE: // 可能是sdcard不存在,或者是 break; default: break; } } @Override public void onUnkonwIOException(BlockDownloader loader, IOException ex) {} }; private List<BlockDownloader> loaders = new ArrayList<BlockDownloader>(); private DownloadRecorder recorder = DownloadRecorder.getInstance(); class TaskRunner implements Runnable { private boolean interrupt = false; private Future<TaskRunner> futrue; public void boundFutrue(Future<TaskRunner> futrue) { this.futrue = futrue; } /** 取消任务 */ public void stop() { innerStopAllLoader(); if (downloadFile != null && downloadFile.getStatus() == DownloadFile.status_download_loading) { downloadFile.setStatus(DownloadFile.status_download_paused); recorder.updateFile(context, downloadFile); notifyFileStatusChange(); } } // 取消停止loader private void innerStopAllLoader() { if (interrupt) { return; } interrupt = true; synchronized (loaders) { for (BlockDownloader b : loaders) { if (b != null) { b.interrupt(); } } loaders.clear(); } if (futrue != null && !futrue.isCancelled()) { futrue.cancel(true); } } @Override public void run() { if (downloadFile.getStatus() != DownloadFile.status_download_loading) { downloadFile.setStatus(DownloadFile.status_download_loading); recorder.updateFile(context, downloadFile); notifyFileStatusChange(); } String sourceUrl = downloadFile.getSourceUrl(); URL url = null; try { url = new URL(sourceUrl); } catch (MalformedURLException e) { e.printStackTrace(); onDownloadError(TaskListener.TASK_ERROR_MALFORMED_URL); return; } synchronized (blocks) { if (blocks.isEmpty()) { // loadBlocksFromCache recorder.getBocks(context, blocks, downloadFile); // if (blocks.size() <= 0) { try { createBlockFromNetFile(downloadFile, url); } catch (CreateBlockException e) { e.printStackTrace(); } finally { if (blocks.size() <= 0) { onDownloadError(TaskListener.TASK_ERROR_INIT_BLOCKS_FAILED); return; } } } } } // synchronized (loaders) { for (DownloadBlock b : blocks) { if (executor.isShutdown()) { onDownloadError(TaskListener.TASK_ERROR_THREAD_POOL_SHUTDOWN); break; } if (!b.isCompleteLoad()) { BlockDownloader bder = new BlockDownloader(blockListener, b); Future<BlockDownloader> f = executor.submit(bder, bder); bder.boundFuture(f); loaders.add(bder); } } } // loopUpdateDownloadInfo(); } /** * 从网络加载分段信息 * * @param file * @param url */ private void createBlockFromNetFile(DownloadFile file, URL url) throws CreateBlockException { if (url == null) { throw new CreateBlockException(); // return; } try { // 分段 final int threadCount = Constants.thread_count; blocks.clear(); // HttpURLConnection c = (HttpURLConnection) url.openConnection(); Utils.configCommonHeader(c, file.getSourceUrl()); c.connect(); // if (c.getResponseCode() == 200) { file.setFileSize(c.getContentLength()); } else { throw new CreateBlockException(); // return; } long perBlockSize = file.getFileSize() / threadCount; String filename = getFileName(c, file.getSourceUrl()); file.setFileName(filename); recorder.updateFile(context, file); // File outFile = file.getFile(); if (outFile != null && !outFile.exists()) { outFile.createNewFile(); } // RandomAccessFile randOut = new RandomAccessFile(outFile, // "rw"); // randOut.setLength(file.getFileSize()); // int lastThread = threadCount - 1; final String sourceUrl = file.getSourceUrl(); for (int i = 0; i < threadCount; i++) { long start = i * perBlockSize; long end; if (i == lastThread) { end = file.getFileSize(); } else { end = (i + 1) * perBlockSize; } DownloadBlock b = new DownloadBlock(file, sourceUrl, start, end, 0); blocks.add(b); } // 保存分段记录到数据库 if (!blocks.isEmpty()) { recorder.addBlocks(context, blocks, file.getSourceUrl()); } } catch (IOException e) { e.printStackTrace(); blocks.clear(); throw new CreateBlockException(); } } private void loopUpdateDownloadInfo() { while (!interrupt) { // 更新进度和速度//block进度的缓存 try { Thread.sleep(Constants.update_speed_interval_time); } catch (InterruptedException e) { e.printStackTrace(); } final int speed = blockTotalIncrease; blockTotalIncrease = 0; long loadedSize = 0; for (DownloadBlock block : blocks) { loadedSize += block.getLoadedSize(); } downloadFile.setLoadedSize(loadedSize); recorder.updateFile(context, downloadFile); // DEBUG-start // int tt = 1024 * 1024; // long totalSize = downloadFile.getFileSize() / tt; // Log.e("ttt", (loadedSize / tt) + "/" + totalSize + " speed:" // + speed / 1024 + "kb/s"); // DEBUG-end // TODO 控制优化 recorder.updateBlockProgress(context, blocks); if (taskListener != null) { taskListener.onProgrees(downloadFile, loadedSize, speed); } // 判断是否下载完毕 if (loadedSize >= downloadFile.getFileSize()) { recorder.removeBlocks(context, downloadFile.getSourceUrl()); downloadFile.setStatus(DownloadFile.status_download_complete); recorder.updateFile(context, downloadFile); notifyFileStatusChange(); if (taskListener != null) { taskListener.onDownloadDone(downloadFile); } innerStopAllLoader(); break; } } } } private void notifyFileStatusChange() { if (taskListener != null) { taskListener.onDownloadFileStatusChanged(downloadFile); } } private void onDownloadError(int taskErrorCode) { int oldStatus = downloadFile.getStatus(); downloadFile.setStatus(DownloadFile.status_download_error); if (oldStatus != DownloadFile.status_download_error) { recorder.updateFile(context, downloadFile); notifyFileStatusChange(); } if (taskListener != null) { taskListener.onDownloadFailed(downloadFile, taskErrorCode); } } private String getFileName(HttpURLConnection conn, String url) { String filename = null; if (url != null) { filename = url.substring(url.lastIndexOf('/') + 1); } if (filename == null || "".equals(filename.trim())) { for (int i = 0; ; i++) { String mine = conn.getHeaderField(i); if (mine == null) break; if ("content-disposition" .equals(conn.getHeaderFieldKey(i).toLowerCase(Locale.getDefault()))) { Matcher m = Pattern.compile(".*filename=(.*)").matcher(mine.toLowerCase(Locale.getDefault())); if (m.find()) return m.group(1); } } filename = UUID.randomUUID() + ".tmp"; } return filename; } public interface TaskListener { /** 无效的URL */ public static final int TASK_ERROR_MALFORMED_URL = 0; public static final int TASK_ERROR_INIT_BLOCKS_FAILED = 1; public static final int TASK_ERROR_THREAD_POOL_SHUTDOWN = 2; public void onProgrees(DownloadFile file, long loadedSize, int speed); public void onDownloadDone(DownloadFile file); public void onDownloadFailed(DownloadFile file, int errorCode); public void onDownloadFileStatusChanged(DownloadFile file); } class CreateBlockException extends Exception { private static final long serialVersionUID = 3459920816008528412L; @Override public String toString() { return "CreateBlockException"; } } }