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;
	}

}


效果圖:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章