Android 多线程下载断点续传

随笔,注释写得很清楚了,如有不明白的地方,相互交流.....

package com.download.service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;

/**
 * 下载线程
 * @author dream
 *
 */
public class DownThread extends Thread {
	/**
	 * 保存文件路径
	 */
	private String filepath;
	/**
	 * 停止线程标识
	 */
	private boolean stopFlag=false;
	/**
	 * 下载开始位置
	 */
	private int startposition;
	/**
	 * 下载结束位置
	 */
	private int endposition;
	/**
	 * 下载链接
	 */
	private String url;
	/**
	 * 下载服务类
	 */
	private HttpDownService hds;
	/**
	 * 线程下载完毕标识
	 */
	private boolean success=false;
	/**
	 * 当前线程下载量
	 */
	private int currentjd=0;
	
	/**
	 * 获取当前线程下载量
	 * @return
	 */
	public int getCurrentjd() {
		return currentjd;
	}

	/**
	 * 当前线程是否下载完毕
	 * @return
	 */
	public boolean isSuccess() {
		return success;
	}

	@Override
	public void run() {
		BufferedReader br=null;
		RandomAccessFile fout=null;
		int oldposition=0;
		try{
			File fstate=null;
			if(FileUtil.Exists(HttpDownService.DIR, this.getName()+".txt")){
				fstate=FileUtil.createFile(HttpDownService.DIR, this.getName()+".txt");
				br=new BufferedReader(new FileReader(fstate));
				String line=br.readLine();
				br.close();
				if(line!=null){
					line=line.trim();
					//是否已完成下载
					if("complete".equals(line)){
						this.currentjd=endposition-startposition;
						this.success=true;
						return;
					}else{
						int newposition=Integer.parseInt(line);
						//若当前开始位置大于下载开始位置则保存下载开始位置(用于计算下载量)
						if(newposition>startposition){
							oldposition=startposition;
							startposition=newposition;
						}
					}
				}
			}else{
				fstate=FileUtil.createFile(HttpDownService.DIR, this.getName()+".txt");
				oldposition=startposition;
			}
			
			
			fout= new RandomAccessFile(this.filepath, "rwd");
			fout.seek(startposition);
			HttpURLConnection http=DownUtil.getDownConnection(this.url, this.startposition, this.endposition);
			InputStream in= http.getInputStream();
			byte[] buff=new byte[1024];
			int len=0;
			while((len=in.read(buff, 0, 1024))>0){
				success=false;
				if(stopFlag){
					if(fout!=null){
						fout.close();
						return;
					}
					return;
				}
				
				fout.write(buff, 0, len);
				//当前线程下载进度
				startposition=startposition+len;
				int jd=startposition-oldposition;
				//刷新当前进度消息
				this.currentjd=jd;
				//把进度存储到存储设备中去
				FileOutputStream fw=new FileOutputStream(fstate);
				fw.write((startposition+"").getBytes());
				fw.close();
				
			}
			//线程下载完毕
			fout.close();

			//将线程下载完毕信息写到磁盘
			FileOutputStream fw=new FileOutputStream(fstate);
			fw.write(("complete").getBytes());
			fw.close();
			
			//下载完毕信号
			this.success=true;
			//更新进度信息
			this.currentjd=endposition-oldposition;
		}catch(Exception e){
			throw new RuntimeException(e);
		}finally{
			try{
				if(fout!=null){
					fout.close();
				}
			}catch(Exception e){
				throw new RuntimeException(e);
			}
		}
		
		
		
	}

	public DownThread(String randomFile,String threadName,int startposition,int endposition,String url,HttpDownService hds){
		this.filepath=randomFile;
		this.setName(threadName);
		this.startposition=startposition;
		this.endposition=endposition;
		this.url=url;
		this.hds=hds;
	}
	
	/**
	 * 停止下载线程
	 */
	public void StopThread(){
		this.stopFlag=true;
	}
}

package com.download.service;

import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 计算各个线程下载开始和结束位置
 * @author dream
 *
 */
public class DownUtil {
	public static long getContentLength(String url){
		try{
			HttpURLConnection http= DownUtil.getDownConnection(url);
			long len=http.getContentLength();
			http.disconnect();
			return len;
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 获取Http连接
	 * @param url
	 * @return
	 */
	public static HttpURLConnection getDownConnection(String url){
		try{
			URL hurl=new URL(url);
			HttpURLConnection http= (HttpURLConnection) hurl.openConnection();
			http.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; WOW64; Trident/6.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729; InfoPath.3; MALCJS)");
			http.setRequestProperty("Connection", "Keep-Alive");
			http.setRequestMethod("GET");
			return http;
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}
	
	/***
	 * 获取Http连接 
	 * @param url
	 * @param startposition
	 * @param endposition
	 * @return
	 */
	public static HttpURLConnection getDownConnection(String url,long startposition,long endposition){
		try{
			URL hurl=new URL(url);
			HttpURLConnection http= (HttpURLConnection) hurl.openConnection();
			http.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; WOW64; Trident/6.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729; InfoPath.3; MALCJS)");
			http.setRequestProperty("Connection", "Keep-Alive");
			http.setRequestMethod("GET");
			http.setRequestProperty("Range", "bytes=" + startposition + "-"+ endposition);
			return http;
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}
}

package com.download.service;

import java.io.File;

import android.os.Environment;

public class FileUtil {
	public static File createFile(String dir,String filename){
		Environment ev=new Environment();
		String diry= ev.getExternalStorageDirectory().getPath()+"/"+dir;
		File fdir=new File(diry);
		if(!fdir.exists()){
			fdir.mkdirs();
		}
		String path= ev.getExternalStorageDirectory().getPath()+"/"+dir+"/"+filename;
		File file=new File(path);
		try{
			if(file.exists()){
				return file;
			}else{
				file.createNewFile();
				return file;
			}
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}
	
	public static boolean Exists(String dir,String filename){
		Environment ev=new Environment();
		String path= ev.getExternalStorageDirectory().getPath()+"/"+dir+"/"+filename;
		File file=new File(path);
		return file.exists();
	}
}

package com.download.service;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import android.os.Handler;
/**
 * 多线程下载/断点续
 * @author dream
 *
 */
public class HttpDownService {
	/**
	 * 线程状态记录文件目录(处于SD卡)
	 */
	public static final String DIR="downtemp";
	/**
	 * 下载进度
	 */
	private int jd=0;
	/**
	 * 下载链接
	 */
	private String url;
	/**
	 * 总大小
	 */
	private int total;
	/**
	 * 获取总大小
	 * @return
	 */
	public int getTotal() {
		return total;
	}
	
	/**
	 * 设置总大小
	 * @param total
	 */
	public void setTotal(int total) {
		this.total = total;
	}

	/**
	 * 开启的下载线程数量(数量越多下载效率越高)
	 */
	private int threadcount;
	/**
	 * 保存文件路径
	 */
	private String filepath;
	/**
	 * 下载线程集合
	 */
	private List<DownThread> list;
	/**
	 * 界面UI handler处理
	 */
	private Handler handler;
	
	/**
	 * 下载状态信息处理
	 */
	private ProcessHandler ph;
	/**
	 * 扫描下载状态的线程
	 */
	private ScanThread st;
	public int getJd() {
		return this.jd;
	}

	public List<DownThread> getList() {
		return list;
	}

	/**
	 * 设置当前下载进度(线程安全)
	 * @param jd
	 * @param oldjd
	 */
	public void setJd(int jd,int oldjd) {
		synchronized (this) {
			this.jd=this.jd-oldjd;
			this.jd = this.jd+ jd;
		}
	}
	
	public HttpDownService(String url,String filepath,Handler handler,ProcessHandler ph){
		this.url=url;
		this.filepath=filepath;
		this.total=(int) DownUtil.getContentLength(url);
		this.handler=handler;
		this.ph=ph;
	}
	
	/***
	 * 启动下载
	 * @param threadCount
	 */
	public void StartDown(int threadCount){
		this.threadcount=threadCount;
		if(this.list==null){
			this.list=new ArrayList<DownThread>();
		}
		this.list.clear();
		int blocksize=this.total/this.threadcount;
		for(int i=0;i<this.threadcount;i++){
			int startposition=i*blocksize;
			int endposition=(i+1)*blocksize;
			if(this.threadcount-i==1){
				endposition=this.total;
			}
			DownThread dt=new DownThread(this.filepath, "e"+i, startposition,endposition, this.url, this);
			dt.start();
			this.list.add(dt);
		}
		st=new ScanThread(this, this.handler, this.ph);
		st.start();
	}
	
	/**
	 * 停止下载
	 */
	public void StopDown(){
		for(DownThread dt:this.list){
			dt.StopThread();
		}
		this.st.StopThread();
	}
	
	/**
	 * 下载是否完成
	 * @return
	 */
	public boolean IsDownComplete(){
		boolean ret=false;
		int i=0;
		for(DownThread dt:this.list){
			if(dt.isSuccess()){
				i++;
			}
		}
		if(this.threadcount==i){
			ret=true;
		}
		return ret;
	}
	
	/**
	 * 下载进度
	 * @return
	 */
	public int DownProcess(){
		int process=0;
		for(DownThread dt:this.list){
			process=process+dt.getCurrentjd();
		}
		return process;
	}
	
	/**
	 * 删除临时文件
	 */
	public void DeleteStateFile(){
		for(DownThread dt:this.list){
			File file=FileUtil.createFile(HttpDownService.DIR, dt.getName()+".txt");
			if(file.exists()){
				file.delete();
			}
		}
	}
	
}

package com.download.service;

import android.os.Handler;
/**
 * 下载信息转发处理
 * @author dream
 *
 */
public interface ProcessHandler {
	public void Process(Handler handler,int total,int current,boolean iscomplete);
}

package com.download.service;

import android.os.Handler;
import android.os.Message;

/**
 * 扫描下载状态,并转发状态信息
 * @author dream
 *
 */
public class ScanThread extends Thread {
	/**
	 * 下载服务类
	 */
	private HttpDownService hds;
	/**
	 * 界面UI Handler处理
	 */
	private Handler handler;
	/**
	 * 线程下载状态处理器
	 */
	private ProcessHandler ph;
	/**
	 * 停止线程标识
	 */
	private boolean Stopflag=false;
	public ScanThread(HttpDownService hds,Handler handler,ProcessHandler ph){
		this.handler=handler;
		this.hds=hds;
		this.ph=ph;
	}
	@Override
	public void run() {
		this.Stopflag=false;
		try{
			while(!Stopflag){
				Thread.sleep(10);
				this.ph.Process(this.handler, this.hds.getTotal(),this.hds.DownProcess(),this.hds.IsDownComplete());
				//当所有线程下载完毕时,转发下载完毕信息
				if(this.hds.IsDownComplete()){
					this.ph.Process(this.handler, this.hds.getTotal(),this.hds.DownProcess(),this.hds.IsDownComplete());
					this.hds.DeleteStateFile();
					this.Stopflag=true;
					return;
				}
			}
		}catch(Exception e){
			throw new RuntimeException(e); 
		}
	}
	
	/**
	 * 停止线程
	 */
	public void StopThread(){
		this.Stopflag=true;
	}

}


效果图:

猜你喜欢

转载自blog.csdn.net/vs2008aspnet/article/details/18849253
今日推荐