實現原理
(1)首先獲得下載文件的長度,然后設置本地文件的長度。
(2)根據文件長度和線程數計算每條線程下載的數據長度和下載位置。
如:文件的長度為6M,線程數為3,那么,每條線程下載的數據長度為2M,每條線程開始下載的位置如下圖所示:
?例如10M大小,使用3個線程來下載,
線程下載的數據長度 ? (10%3 == 0 ? 10/3:10/3+1) ,第1,2個線程下載長度是4M,第三個線程下載長度為2M
下載開始位置:線程id*每條線程下載的數據長度 = ?
下載結束位置:(線程id+1)*每條線程下載的數據長度-1=?
Activity代碼
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@SuppressLint("HandlerLeak")
public static Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int size = msg.getData().getInt("size");
// progressbar.setProgress();
float temp = (float) size / (float)progressbar.getMax();
int progress = (int) (temp * 100);
if (progress == 100) {
Log.e("TAG", "handleMessage: " + "下載完成");
}
tv_result.setText("下載進度:" + progress + "%");
}
};
private EditText ed;
private Button btn_download;
private static ProgressBar progressbar;
private static TextView tv_result;
private String urlstring;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
ed = (EditText) findViewById(R.id.ed);
btn_download = (Button) findViewById(R.id.btn_download);
progressbar = (ProgressBar) findViewById(R.id.progressbar);
tv_result = (TextView) findViewById(R.id.tv_result);
btn_download.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_download:
submit();
download();
break;
}
}
//下載文件
private void download() {
//獲取sd卡路徑
String path = Environment.getExternalStorageDirectory() + "/morethreaddownload/";
File file = new File(path);
if (!file.exists()) {
// 如果文件目錄不存在,就創建此文件夾
file.mkdir();
}
// 指定下載的大文件的名稱
String filename = "fulin.apk";
// 要下載的文件路徑
String filepath = path + filename;
// 線程的數量
int threadcount = 5;
// 開啟子線程下載文件(獲取文件的長度)
DownLoadTask task = new DownLoadTask(progressbar,urlstring,filepath,threadcount);
task.start();
}
private void submit() {
//輸入框中用戶輸入的網絡路徑
urlstring = ed.getText().toString().trim();
if (TextUtils.isEmpty(urlstring)) {
Toast.makeText(this, "edString不能為空", Toast.LENGTH_SHORT).show();
return;
}
}
}
DownLoadTask類
public class DownLoadTask extends Thread {
// 網路文件的下載地址
private String downloadUrl;
// 保存文件的路徑
private String filePath;
// 線程的數量
private int threadcount;
// 進度條
private ProgressBar progressBar;
// 文件的大小
public static int filesize;
// 每個線程的下載量
private int blockSize;
//構造方法
public DownLoadTask(ProgressBar progressBar, String downloadUrl, String filePath, int threadcount) {
this.progressBar=progressBar;
this.downloadUrl = downloadUrl;
this.filePath = filePath;
this.threadcount = threadcount;
}
@Override
public void run() {
super.run();
// 線程數組
FileDownloadThread[] threads = new FileDownloadThread[threadcount];
try {
URL url = new URL(downloadUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
if (conn.getResponseCode() == 200) {
// 讀取下載文件的總長度
filesize = conn.getContentLength();
if (filesize <= 0) {
Log.e(TAG, "run: " + "讀取文件失敗");
return;
}
// 設置progressbar的最大值
progressBar.setMax(filesize);
//
// 計算每條線程下載的數據長度
blockSize = filesize % threadcount == 0 ? filesize / threadcount : filesize / threadcount + 1;
//sd卡里邊指定的保存網絡端下載下來的文件
File file = new File(filePath);
// 啟動每個線程,分別下載所分配的長度
for (int i = 0; i < threadcount; i++) {
threads[i] = new FileDownloadThread(file,url,(i+1),blockSize);
threads[i].setName("Thread:" + i);
threads[i].start();
}
//是否下載完成
boolean isfinished = false;
// 下載的總長度
int downloadAllsize = 0;
while (!isfinished) {
isfinished = true;
downloadAllsize = 0;
for (int i = 0; i < threadcount; i++) {
downloadAllsize += threads[i].getDownloadlenght();
if (!threads[i].isCompleted()) {
isfinished = false;
}
}
// 通知handler去更新
Message message = new Message();
message.getData().putInt("size", downloadAllsize);
MainActivity.handler.sendMessage(message);
Thread.sleep(1000);
}
Log.e(TAG, "run:下載的總大小: "+ downloadAllsize);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class FileDownloadThread extends Thread {
/**
* 文件保存路徑
*/
private File file;
/**
* 文件下載路徑 (文件網址)
*/
private URL downloadUrl;
/**
* 當前下載線程ID
*/
private int threadId;
/**
* 線程下載數據長度
*/
private int blockSize;
private int downloadlenght;
private boolean isCompleted;
public FileDownloadThread(File file, URL downloadUrl, int threadId, int blockSize) {
this.file = file;
this.downloadUrl = downloadUrl;
this.threadId = threadId;
this.blockSize = blockSize;
}
@Override
public void run() {
super.run();
BufferedInputStream bis=null;
RandomAccessFile raf=null;
try {
HttpURLConnection conn = (HttpURLConnection) downloadUrl.openConnection();
conn.setAllowUserInteraction(true);
// 開始的位置
int startpos=blockSize*(threadId-1);
int endpos=blockSize*threadId-1;
// 設置當前線程下載的開始點和結束點
conn.setRequestProperty("Range","bytes="+startpos+"-"+endpos);
byte[] buffer=new byte[1024];
bis=new BufferedInputStream(conn.getInputStream());
raf=new RandomAccessFile(file,"rwd");
raf.seek(startpos);
int len=0;
while((len=bis.read(buffer,0,1024))!=-1){
raf.write(buffer,0,len);
downloadlenght+=len;
}
isCompleted=true;
} catch (IOException e) {
e.printStackTrace();
}
}
//每個線程下載的數據長度
public int getDownloadlenght() {
return downloadlenght;
}
//返回線程是否下載完成
public boolean isCompleted() {
return isCompleted;
}
}
FileDownloadThread類
class FileDownloadThread extends Thread {
/**
* 文件保存路徑
*/
private File file;
/**
* 文件下載路徑 (文件網址)
*/
private URL downloadUrl;
/**
* 當前下載線程ID
*/
private int threadId;
/**
* 線程下載數據長度
*/
private int blockSize;
private int downloadlenght;
private boolean isCompleted;
public FileDownloadThread(File file, URL downloadUrl, int threadId, int blockSize) {
this.file = file;
this.downloadUrl = downloadUrl;
this.threadId = threadId;
this.blockSize = blockSize;
}
@Override
public void run() {
super.run();
BufferedInputStream bis=null;
RandomAccessFile raf=null;
try {
HttpURLConnection conn = (HttpURLConnection) downloadUrl.openConnection();
conn.setAllowUserInteraction(true);
// 開始的位置
int startpos=blockSize*(threadId-1);
int endpos=blockSize*threadId-1;
// 設置當前線程下載的開始點和結束點
conn.setRequestProperty("Range","bytes="+startpos+"-"+endpos);
byte[] buffer=new byte[1024];
bis=new BufferedInputStream(conn.getInputStream());
raf=new RandomAccessFile(file,"rwd");
raf.seek(startpos);
int len=0;
while((len=bis.read(buffer,0,1024))!=-1){
raf.write(buffer,0,len);
downloadlenght+=len;
}
isCompleted=true;
} catch (IOException e) {
e.printStackTrace();
}
}
//每個線程下載的數據長度
public int getDownloadlenght() {
return downloadlenght;
}
//返回線程是否下載完成
public boolean isCompleted() {
return isCompleted;
}
}
詳細配置信息可以參考我寫的這篇文章:http://blog.ncmem.com/wordpress/2019/08/28/net%e6%96%87%e4%bb%b6%e6%89%b9%e9%87%8f%e4%b8%8b%e8%bd%bd/