多线程下载和断点续传用HttpUrlConnection和OkHttp都可以实现
这里说的是用OkHttp来实现的
以下是代码,多线程下载,暂停开始,断点续传,进度条显示
public class testActivity extends Activity {
private String name="aaa";
private int pwd=123;
private final String getUrl="http://139.129.110.99:8901/test?name="+name+"&pwd="+pwd;
private final String postUrl="http://139.129.110.99:8901/posttest";
private final String sourceUrl="http://api.jisuapi.com/news/get?channel=头条&start=0&num=10&appkey=e06ac19faa18ef4a";
public final String fieUrl="http://139.129.110.99:81/witraffic_app.rar";
private ProgressBar progressBar,progressBar1,progressBar2,progressBar3;
private OkHttpClient client=new OkHttpClient();
private Call call;
//设置线程的数量
private final static int threadCount=3;
//正在下载的线程
private int runningThread;
//设置下载的文件的路径
public String filePath;
//总的下载进度
public int totalCount=0;
//文件总大小
public long total;
//判断是否正在下载
public Boolean isRunning=false;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
progressBar=(ProgressBar)findViewById(R.id.progress);
button=(Button)findViewById(R.id.btn);
runningThread=threadCount;
//创建下载的文件
File file=new File(Environment.getExternalStorageDirectory(),"aaa");
file.mkdirs();
File file1=new File(file,"aaa.rar");
try {
file1.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
//点击这个按钮的时候开始下载或者继续下载,对应button的点击事件
public void click1(View v)
{
if(!isRunning) {
//这次请求就为了获取文件长度
Request request = new Request.Builder()
.url(fieUrl)
.tag("a")
.build();
call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.i("test", e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//1 获取到文件的大小
total = response.body().contentLength();
//设置总大小
progressBar.setMax((int) total);
//2 创建和服务器大小一样的文件,同时为他分配和服务器上文件大小一样的空间
//支持对文件随机读写的类,第一个参数传入文件的地址,第二个参数是模式,rwd代表同步到底层设备
//随机读写的意思是可以从任何的位置开始读写
filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/aaa/aaa.rar";
RandomAccessFile file = new RandomAccessFile(filePath, "rwd");
//设置文件的大小
file.setLength(total);
Log.i("test", "线程" + total);
//3 计算每个线程下载的大小和开始的长度
/*
计算方法说明:
文件下载的起点是从0开始的,所以如果一个文件共10000bit,那么下载的位置就是0~9999
首先用总大小除以线程数量,这两者都是整型,做除法后得到的也是整型
得到的就是处理最后一个线程以外每个线程需要下载的长度
文件的地址是从0开始的,所以第一个线程下载的位置就是0*blockSize~1*blockSize-1
第二个就是1*blockSize~2*blockSize-1
......
第n个就是(n-1)*blockSize~n*blockSize-1
.....
最后一个线程下载区间就是,假设一共三个线程
2*blockSize~文件末尾
*/
/*
所以第一个线程的开始位置为0,结束位置就是1*blockSize-1
第二个线程开始位置为1*blockSize,结束位置就是2*blockSize-1
....
第n个线程开始位置就是(n-1)*blockSize,结束位置就是n*blockSize-1
假设一共三个线程,最后一个线程开始位置就是,2*blockSize~(文件总长度-1)
*/
//3.1 算出除了最后一个线程外每个线程下载的大小
long blockSize = total / threadCount;
Log.i("test", "" + blockSize);
//3.2 开多个线程进行下载
for (int i = 0; i < threadCount; i++) {
//每个线程的开始位置
long startIndex = i * blockSize;
//每个线程的结束位置
long endIndex = (i + 1) * blockSize - 1;
//如果是最后一个线程,那么结束位置是文件的末尾
if (i == (threadCount - 1)) {
endIndex = total - 1;
}
Log.i("test", "线程" + i + "开始位置" + startIndex + "结束位置:" + endIndex);
//开启线程开始下载,单写一个DownLoadThread类,直接传入参数开始下载
new DownLoadThread(startIndex, endIndex, i).start();
}
isRunning = true;
}
});
button.setText("暂停");
}
else
{
CancelTag("a");
button.setText("开始");
isRunning=false;
}
}
//下载文件的线程的代码
public class DownLoadThread extends Thread
{
public long startIndex;
public long endIndex;
public long threadId;
public DownLoadThread(long startIndex,long endIndex,long threadId)
{
this.startIndex=startIndex;
this.endIndex=endIndex;
this.threadId=threadId;
}
@Override
public void run() {
//创建存放开始位置的文件
final File startIndexFile=new File(Environment.getExternalStorageDirectory()+"/aaa",threadId+".txt");
if(!startIndexFile.exists())
{
try {
startIndexFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
//判断是第一次下载还是断点后继续下载
try {
FileInputStream inputStream=new FileInputStream(startIndexFile);
BufferedReader reader=new BufferedReader(new InputStreamReader(inputStream));
String currentIndex=reader.readLine();
if(currentIndex!=null)
{
startIndex=Integer.parseInt(currentIndex);
Log.i("test","线程"+threadId+"开始位置:"+startIndex+"结束位置:"+endIndex);
}
inputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e)
{
e.printStackTrace();
}
//这个判断很重要,在结束的时候暂停很可能导致开始位置大于结束位置
if(startIndex<=endIndex)
{
//这里开始请求数据,每个线程请求的都是部分数据,所以要设置一个头
//这个头的名为Range,参数是下载的起止位置,格式为"bytes=开始位置-结束位置"
//这时返回的状态码为206,206代表请求部分数据
//同时设置一个tag标签,用于暂停下载时使用
final Request request=new Request.Builder()
.url(fieUrl)
.tag("a")
.addHeader("Range","bytes="+startIndex+"-"+endIndex)
.build();
Call call=client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//这里开始处理接收文件的逻辑
//记录该线程当前下载的大小
long currentCount=0;
//记录当前的位置
long currentPosition=0;
//首先再拿到之前创建的空文件
RandomAccessFile file=new RandomAccessFile(filePath,"rwd");
//设置开始写入的位置
file.seek(startIndex);
int len=-1;
InputStream inputStream=response.body().byteStream();
byte[] bytes=new byte[1024*1024];
while((len=inputStream.read(bytes))!=-1)
{
//RandomAccessFile类同样有write方法,写入文件,相当于输出流,也需要关闭
file.write(bytes,0,len);
//当前线程下载的总大小
currentCount+=len;
//如果下载暂停了以后新的下载位置
currentPosition=startIndex+currentCount;
//用RandomAccessFile来记录位置是因为它可以用rwd模式将数据同步到底层设备,
//避免数据同步不上
RandomAccessFile raf=new RandomAccessFile(startIndexFile,"rwd");
raf.write(String.valueOf(currentPosition).getBytes());
raf.close();
//设置下载进度,progress可以直接在子线程更新UI
synchronized (DownLoadThread.class)
{
totalCount+=len;
progressBar.setProgress(totalCount);
if(totalCount==total)
{
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(testActivity.this,"下载完毕",Toast.LENGTH_SHORT).show();
}
});
}
}
}
//关闭RandomAccessFile
file.close();
Log.i("test","线程"+threadId+"下载完毕");
//每次下载完毕一个线程runningThread就减一,当runningThread为0时,代表所有线程都下载完毕
//为DownLoadThread在操作runningThread--时加锁,在多个线程同时操作runningThread--时保证先后顺序
synchronized (DownLoadThread.class)
{
runningThread--;
//当所有线程都下载完毕时,删除存储开始位置的文件
if(runningThread==0)
{
for(int i=0;i