android多线程断点下载
多线程断电xia下载,通过设置线程的数量,动态获取下载文件线程的个数,这是本人用于练习所写demo,注释很详细,用于初学者参考使用。
MainActivity.java页面
package com.dahui.download;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
public class MainActivity extends Activity {
private EditText et_path;
private EditText et_threadCount;
private LinearLayout ll_pb_layout;
private String path;
private static int runningThread; //代表当前正在运行的线程
private int threadCount;
private List<ProgressBar> pbList;//用来存进度条的引用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_path = (EditText) findViewById(R.id.et_path);
et_threadCount = (EditText) findViewById(R.id.et_threadCount);
ll_pb_layout = (LinearLayout) findViewById(R.id.ll_pb);
pbList = new ArrayList<ProgressBar>();
}
//点击下载按钮
public void click(View v){
path = et_path.getText().toString().trim();
//[3]获取线程数量
String threadCountt = et_threadCount.getText().toString().trim();
//[3.1]先移除之前添加的进度条
ll_pb_layout.removeAllViews();
threadCount = Integer.parseInt(threadCountt);
pbList.clear();//每次点击清空集合
for (int i = 0; i < threadCount; i++) {
//[3.1]把定义的item布局转换成view对象
ProgressBar pbView = (ProgressBar) View.inflate(getApplicationContext(), R.layout.item, null);
//[3.2]把pbView添加到集合中
pbList.add(pbView);
//[4]动态添加进度条
ll_pb_layout.addView(pbView);
}
//[5]开始移植 联网
new Thread(){public void run() {
//【1】★★★★★★获取服务器文件大小 要计算每个线程开始位置和结束位置
try {
//String path = "http://192.168.231.2:8080/new1.xml";
URL url = new URL(path);
//[2.3]拿到HttpURLConnection对象 用于发送或接受数据
HttpURLConnection coon = (HttpURLConnection) url.openConnection();
//[2.4]设置发送get请求
coon.setRequestMethod("GET");//get大写 默认就是get
//请求超时时间
coon.setConnectTimeout(5000);
//[2.6]获取服务器返回的状态码
int code = coon.getResponseCode();
//[2.7]如果code==200 说明请求成功
if (code == 200) {
int length = coon.getContentLength();
runningThread = threadCount; //把线程的数量赋值给正在运行的线程
System.out.println("length"+length);
//【2】★★★★★★创建一个大小和服务器一模一样的文件 目的是把空间提前申请出来
RandomAccessFile rafAceAccessFile = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+"/"+getFilename(path), "rw");
rafAceAccessFile.setLength(length);
//算出每个线程下载的大小
int blockSize = length / threadCount;
//【3】★★★★★★计算每个线程开始位置和结束位置
for (int i = 0; i < threadCount; i++) {
int startIndex = i*blockSize; //线程开始位置
int endIndex = (i+1)*blockSize - 1;
if (i == threadCount -1) {
endIndex = length - 1;
}
System.out.println("线程Id:"+i +"理论下载位置"+":"+startIndex+"-----"+endIndex);
//【4】★★★★★★开启线程去服务器下载文件
DownLoadThread downLoadThread = new DownLoadThread(startIndex, endIndex, i);
downLoadThread.start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
};}.start();
}
public String getFilename(String path){
int start = path.lastIndexOf("/")+1;
return path.substring(start);
}
private class DownLoadThread extends Thread{
//通过构造方法把每个线程下载的开始位置和结束位置传递出来
private int startIndex;
private int endIndex;
private int threadId;
private int PbMaxSize; //代表当前线程下载的最大值
//如果中断过 则获取上次下载位置
private int pblastposition;
public DownLoadThread(int startIndex,int endIndex,int threadId){
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
}
@Override
public void run() {
//实现去服务器下载文件的逻辑
try {
//[0]计算当前进度条的最大值
PbMaxSize = endIndex-startIndex;
URL url = new URL(path);
//[2.3]拿到HttpURLConnection对象 用于发送或接受数据
HttpURLConnection coon = (HttpURLConnection) url
.openConnection();
//[2.4]设置发送get请求
coon.setRequestMethod("GET");//get大写 默认就是get
//请求超时时间
coon.setConnectTimeout(5000);
//★★★★★★★如果中间断过,继续上次的位置下载 从文件中读取上次下载的位置
File file = new File(Environment.getExternalStorageDirectory().getPath()+"/"+getFilename(path)+threadId+".txt");
if (file.exists()&&file.length()>0) {
FileInputStream fis = new FileInputStream(file);
BufferedReader buff = new BufferedReader(new InputStreamReader(fis));
String lastPositionn = buff.readLine();//读取出来的内容就是上一次下载的位置
int lastPosition = Integer.parseInt(lastPositionn);
//★★给我们定义的进度条赋值
pblastposition = lastPosition - startIndex;
//☆☆☆☆☆☆要改变一下startIndex位置
startIndex = lastPosition;
System.out.println("线程Id:"+threadId +"真实下载位置"+":"+startIndex+"-----"+endIndex);
fis.close();
}
//设置请求头Range (告诉服务器每个线程的开始下载和结束位置)
coon.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
//[2.6]获取服务器返回的状态码
int code = coon.getResponseCode();
//[2.7]如果code==200 说明请求成功 206代表请求部分资源成功
if (code == 206) {
//创建随机读写文件对象
RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+"/"+getFilename(path), "rw");
//每个线程从自己的位置开始写
raf.seek(startIndex);
//[2.8]获取服务器返回的数据 以流的形式返回(要把流转换成字符串,此操作比较常见,所以把它做成工具类)
InputStream in = coon.getInputStream();//存的是UCBrowser.exe
//9把数据写到文件中
int len= -1;
byte[] buffer = new byte[1024*1024];
int total = 0;//代表当前线程下载的大小
while ((len = in.read(buffer))!= -1) {
raf.write(buffer,0,len);
total+=len;
//10.实现断点续传 记录当前下载的位置
int currentThreadPosition = startIndex+total;//把这个位置存起来
//11.用来存当前线程下载的位置
RandomAccessFile raff = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+"/"+getFilename(path)+threadId+".txt", "rwd");
raff.write(String.valueOf(currentThreadPosition).getBytes());
raff.close();
//设置进度条的最大值和最小值
pbList.get(threadId).setMax(PbMaxSize);//设置进度条最大值
pbList.get(threadId).setProgress(pblastposition+total);//设置进度条当前进度
}
raf.close(); //关闭流 释放资源
System.out.println("线程id:"+threadId+"----下载完毕!");
//下载完毕删除断点保存的.txt文件
synchronized (this) {
runningThread--;
if(runningThread == 0){
//说明所有线程执行完毕
for (int i = 0; i < threadCount; i++) {
File delteFile = new File(Environment.getExternalStorageDirectory().getPath()+"/"+getFilename(path)+i+".txt");
delteFile.delete();
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
布局页面:activity_main.xml页面
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<EditText
android:id="@+id/et_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="http://192.168.231.2:8080/UCBrowser.exe"
android:hint="请输入下载的路径"
/>
<EditText
android:id="@+id/et_threadCount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入线程的数量"
/>
<Button
android:onClick="click"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下载"
/>
<LinearLayout
android:id="@+id/ll_pb"
android:background="#080E12"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
</LinearLayout>
</LinearLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/progressBar1"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
结果截图:
资源源码包下载地址:https://download.****.net/download/qq_38993002/10759021