• Android多线程下载大文件解析


    1、多线程介绍

            用过迅雷的同学都知道。迅雷有个功能叫做多线程。另一个叫离线下载,我们这里重点介绍一下多线程下载。多线程,顾名思义就是非常多歌线程同一时候在执行,为什么要提出多线程这个概念呢?由于有时候一个线程下载太慢了。举个样例,比方小时候常做的数学题,一个人挖沟须要15天,那么两个人对着挖呢?

            当然数学题上面两个人的效率是不一样的,我们这里把这个问题简化了一下。仅仅是想大家明确,什么是多线程,为什么有多线程。

            在多线程上出现过一个问题,为什么有要提出多线程?事实上提出多线程是为了充分利用CPU的硬件资源,解决应用程序等待的问题。多线程是为了同步完毕多项任务,不是为了提高执行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间须要完毕多项任务的时候实现的。


    2、思路
    (1)获取网络连接
    (2)初始化多线程下载信息,開始下载
    (3)开辟硬盘空间,用于存放数据资源
    (4)把从网络获取的数据放入申请的空间中
    (5)完成下载,关闭资源链接

    给出一个下载400M电影的示意图,例如以下所看到的:


    RandomAccessFile支持随机的訪问
    HTTP的Range头字段指定每一个线程从文件的什么位置開始下载。


    3、代码解析

    3.1 设置须要下载文件的信息

    RandomAccessFile randOut = new RandomAccessFile(this.saveFile, "rwd");
    if(this.fileSize>0){ 
    	randOut.setLength(this.fileSize);//设置文件的大小
    }
    randOut.close();	//关闭该文件,使设置生效

    3.2 设置下载链接,而且開始划分下载部分

    URL url = new URL(this.downloadUrl);
    if(this.data.size() != this.threads.length){	//假设原先未曾下载或者原先的下载线程数与如今的线程数不一致
    	this.data.clear();	//将数据置空
    	for (int i = 0; i < this.threads.length; i++) {	//遍历线程池
    		this.data.put(i+1, 0);//初始化每条线程已经下载的数据长度为0
    	}
    	this.downloadedSize = 0;	//设置已经下载的长度为0
    }


    3.3 開始下载文件

    for (int i = 0; i < this.threads.length; i++) {
    	//通过特定的线程ID获取该线程已经下载的数据长度
    	int downloadedLength = this.data.get(i+1);
    	//推断线程是否已经完毕下载,否则继续下载
    	if(downloadedLength < this.block && this.downloadedSize < this.fileSize){
    		//初始化特定id的线程
    		this.threads[i] = new DownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1);
    		//设置线程的优先级,Thread.NORM_PRIORITY = 5 Thread.MIN_PRIORITY = 1 Thread.MAX_PRIORITY = 10
    		this.threads[i].setPriority(7);
    		//启动线程
    		this.threads[i].start();
    	}else{
    		this.threads[i] = null;	//表明在线程已经完毕下载任务
    	}
    }


    3.4 监听文件是否下载完毕以及完毕之后的操作

    fileService.delete(this.downloadUrl);	//假设存在下载记录,删除它们。然后又一次加入
    fileService.save(this.downloadUrl, this.data);	//把已经下载的实时数据写入数据库


    boolean notFinished = true;//下载未完毕
    // 循环推断所有线程是否完毕下载
    while (notFinished) {
    	notFinished = false;//假定所有线程下载完毕
    	for (int i = 0; i < this.threads.length; i++){
    		if (this.threads[i] != null && !this.threads[i].isFinished()) {//假设发现线程未完毕下载
    			notFinished = true;//设置标志为下载没有完毕
    			//假设下载失败,再又一次在已经下载的数据长度的基础上下载
    			if(this.threads[i].getDownloadedLength() == -1){
    				//又一次开辟下载线程。代码与上面一致
    			}
    		}
    	}				
    	if(listener!=null){
    		listener.onDownloadSize(this.downloadedSize);
    	}//通知眼下已经下载完毕的数据长度
    }
    
    //下载完毕删除记录
    if(downloadedSize == this.fileSize){
    	fileService.delete(this.downloadUrl);
    }

    4、断点续传

            断点续传是说在下载的时候,我们由于某些原因,导致了下载的暂停。比方在电脑上,我们的电脑突然断电了,手机上的网络中断了,都会导致当前的下载任务终止,那么当我们再次回来的时候。程序应该是能够继续下载的,不然前面下载的资源就都浪费了。


            依据上面的描写叙述。我们应该能够知道。实现断点续传,关键是实现下载的数据存储在数据库中,等到之后我们程序再次进入的时候,会到数据库中去查询一下数据。然后接着继续下载。

    而存储数据到数据库并非太复杂,难的是怎样识别程序的哪些数据被下载了。哪些数据是没有下载的。这里,我们在下载的时候使用了下载的线程id做识别。
            假设该线程id的数据没有被完整下载。应该是不会存储到数据库的,那么这一部分的数据就要又一次下载,在下载完毕之后,数据拼接起来就是一个完整的文件了。






  • 相关阅读:
    MySQL_Key值(MUL、PRI、NUL)
    MySQL删除foreign key_ERROR 1025 (HY000): Error on rename of './test_20180206/cc' to './test_20180206/#sql2-9ac-e' (errno: 152)
    MySQL-ISNULL()、IFNULL()和NULLIF()函数
    MySQL复制表-SELECT INTO FROM
    MySQL复制表-INSERT INTO SELECT
    MySQL复制表-CREATE SELECT
    MySQL_ALTER命令
    CentOS设置防火墙
    宿主机连wifi,虚拟机联网设置步骤
    二叉树遍历
  • 原文地址:https://www.cnblogs.com/lytwajue/p/6851908.html
Copyright © 2020-2023  润新知