Android多线程断点下载

来源:互联网 发布:东莞证券软件下载 编辑:程序博客网 时间:2024/04/28 02:09

        因为自己还是初学者,所以现在研究的都是一些比较初级的问题,有助于我们初学者更好的入门,下面我们就初步分析一下多线程断点下载。首先大概说一下多线程断点下载的步骤,然后根据步骤,我们可以更方便的去写代码。

1、获知文件的大小,getContentLength()

2、在本地创建一个和下载资源同等大小的空文件,RandomAccessFile权限设置为可读可写

3、将要下载的文件分割为几个部分,看你需要几条线程来下载,设置每个线程下载多大

4、开启多线程下载,写入本地空文件的对应位置,通过Range字段来制定下载的起始位置,setRequestProperty("Range","byte="+?+"-"+?)

5、在每个线程下载数据的同时,每当下载一点点数据就保存下载的位置,如果程序下载到一半退出了,下次启动时可以读取文件,接 着上次的下载继续进行,通过seek()来记录上次下载结束的位置为这次下载的起始位置

6、所有的线程下载完成整个下载就完成

MainActivity 

package com.itheima.multdown;


import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;


import android.app.Activity;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Toast;


public class MainActivity extends Activity {
private static final int MAKE_TOAST = 0;
private EditText et_path;
private EditText et_threadcount;
private LinearLayout ll;
private String path = null;
int blocksize;
int threadcount;
int runthread;
boolean isDown =false;
List<ProgressBar> list = new ArrayList<ProgressBar>();
SharedPreferences sps;

Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MAKE_TOAST:
Toast.makeText(MainActivity.this,(String)msg.obj, 0).show();
break;


default:
break;
}
};
};

@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 = (LinearLayout) findViewById(R.id.ll);
sps = getSharedPreferences("progress", MODE_PRIVATE);
}


public void down(View v){
if(isDown){
return;
}
isDown = true;

//linerlayout清空所有进度条
ll.removeAllViews();
//清空集合中的ProgressBar
list.clear();

//获取要下载的地址,检查
path = et_path.getText().toString().trim();
if(TextUtils.isEmpty(path)){
Toast.makeText(this, "路径不能为空!", 0).show();
return;
}
//获取要开启的线程数,默认是3
String tc = et_threadcount.getText().toString().trim();
threadcount = TextUtils.isEmpty(tc) ? 3 : Integer.parseInt(tc) ;

//创建出进度条,填充到ll中
for(int i=0;i<threadcount;i++){
ProgressBar pb = (ProgressBar) View.inflate(this, R.layout.pb, null);
list.add(pb);
ll.addView(pb);
}
//开启线程,准备多线程下载
new Thread(){
public void run() {
try {
//1.获取资源的大小
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
int code = conn.getResponseCode();
if(code == 200){
//--资源的大小
int length = conn.getContentLength();
//--每块的大小
blocksize = length / threadcount;
//--设置剩余的线程数等于开启的线程数
runthread = threadcount;
//--循环计算,每个线程下载从begin到end
for(int i = 1;i<=threadcount;i++){

int begin = sps.getInt(i+"", (i - 1) * blocksize);
int end = (i == threadcount ? length - 1 : i*blocksize -1); 
//--开启子线程,分别进行下载自己块中的内容
startThreadDown(i,begin,end);
}
}else{
makeToast("访问服务器出错!状态码"+code);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}

}
/**
* 开启子线程进行下载,从begin到end,写入到文件中的对应位置
* @param i
* @param begin
* @param end
*/
private void startThreadDown(final int i, final int begin, final int end) {
System.out.println("线程"+i+"开始下载了...从"+begin+"-->"+end);
new Thread(){
public void run() {
try {
//--访问资源
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//--下载指定块的数据
conn.setRequestProperty("Range", "bytes="+begin+"-"+end);
int code = conn.getResponseCode();
if(code == 206){
//--移动游标,写出到文件中的对应位置
InputStream in = conn.getInputStream();
RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+getFileName(path), "rwd");
raf.seek(begin);
byte[] bs = new byte[10240];
int len = 0;

int ps = 0;
while((len = in.read(bs))!=-1){
raf.write(bs,0,len);
//-----------------------??????????????????????????
//--每下载一点点,更新进度条
ProgressBar pb = list.get(i-1);
ps = ps + len;
int tbegin = ((i-1)*blocksize);
pb.setMax(end - tbegin);
pb.setProgress(begin-tbegin+ps);
//--每下载一点点数据,记录下载的位置
Editor editor = sps.edit();
editor.putInt(i+"",begin+ps);
editor.commit();
}
in.close();
raf.close();


//--线程下载完,运行的线程数减1
runthread -- ;
if(runthread == 0 ){
//--没有运行着的线程了,说明下载完成了
makeToast("下载完成了!!");
//--删除sps中的信息
Editor editor = sps.edit();
editor.clear();
editor.commit();
//--标记打开,可以重新下载
isDown = false;
}
}else{
makeToast("访问服务器出错!状态码"+code);
}

} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally{
//--删除sps中的信息
Editor editor = sps.edit();
editor.clear();
editor.commit();
}
};

}.start();
};

}.start();

}
public void makeToast(String msg){
Message msgx = Message.obtain();
msgx.obj = msg;
msgx.what = MAKE_TOAST;
handler.sendMessage(msgx);
}
private String getFileName(String path){
return path.substring(path.lastIndexOf("/"));
}
}

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" >


    <EditText
        android:id="@+id/et_path"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入要下载的资源地址"
        android:text="http://192.168.16.54/1.zip"
         >
    </EditText>


    <EditText
        android:id="@+id/et_threadcount"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入并发线程数,默认是3条线程" />


    <Button
        android:id="@+id/but"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="点击下载" 
        android:onClick="down"
        />

pb.xml
    <LinearLayout
        android:id="@+id/ll"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >
    </LinearLayout>

</LinearLayout>


<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pb"
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

1 0
原创粉丝点击