您的位置:首页 > 移动开发 > Android开发

Andriod 实现多线程断点下载demo

2014-05-02 17:25 453 查看
源码下载↓↓↓

实现功能大体思路:

1.首先根据线程数量,来划分每个线程下载文件的位置;

公式:开始位置=(线程序号-1)*(文件大小/线程数量)

    结束位置= 线程序号*(文件大小/线程数量)-1

比如:文件大小是1000字节,用2个线程来下载,那么第一个线程下载的位置就是0-499,第二个就是500-1000

            0 = (1-1)*(1000/2);

499 = 1*(1000/2)-1;

2.在发出http请求的时候设置请求参数,告诉服务器返回部分资源;con.setRequestProperty("Range","bytes=" + startIndex + "-"+endIndex);

3.下载文件的时候每个线程生成一个相应的下载记录,记录下载量,从而实现断点下载的功能;

先来个效果图:



好了,其他就不说了,上代码;

package com.example.down;

import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.os.Build;

public class MainActivity extends Activity {
public static final int SUCCESS = 0;
public static final int JD = 1;
EditText editText;
TextView textView;
ProgressBar bar;
Handler handler = new Handler(){

@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);

switch (msg.what) {
case SUCCESS:
Toast.makeText(getApplicationContext(), "下载成功", 0).show();
break;
case JD:
Bundle b = msg.getData();
textView.setText("下载进度:"+b.getString("jd"));
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = (EditText)findViewById(R.id.et);
bar = (ProgressBar)findViewById(R.id.pb);
textView = (TextView)findViewById(R.id.tv);
}

public void click(View view){
final String url = editText.getText().toString().trim();
if(TextUtils.isEmpty(url)){
Toast.makeText(this, "请输入下载地址", 0).show();
}else{
bar.setProgress(0);
bar.setMax(0);
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
DownLoad downLoad = new DownLoad(url, MainActivity.this, bar);
try {
downLoad.start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(MainActivity.this, "下载异常", 0).show();
}
}
}).start();
}
}
}

package com.example.down;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

import android.app.Activity;
import android.os.Bundle;
import android.os.Message;
import android.widget.ProgressBar;
import android.widget.Toast;
public class DownLoad {

Integer downTotal = 0;
int totals = 0;
int threadTotal = 3;
String urlStr = "http://192.168.2.101:8080/QQ.exe";
String savePath = "/sdcard/QQ.exe";
Object obj;
ProgressBar bar;
public DownLoad(String url,Object o,ProgressBar bar){
urlStr = url;
this.obj = o;
this.bar = bar;
}

public void start() throws Exception {
URL url = new URL(urlStr);
HttpURLConnection  connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
int code = connection.getResponseCode();
if(code == 200){
//获取下载文件大小
int dataLength = connection.getContentLength();
totals = dataLength;
bar.setMax(totals);
//创建下载到本地的文件大小,RandomAccessFile类支持文件随机写入"rwd"模式表示直接写入磁盘,不缓存到内存中
RandomAccessFile raf = new RandomAccessFile(savePath,"rwd");
raf.setLength(dataLength);
raf.close();
//计算每个线程下载文件的起始位置
int bolckSize = dataLength / threadTotal;
for(int threadId  = 1;threadId <= threadTotal;threadId++){
int startIndex = (threadId-1) * bolckSize;//线程下载的起点位置
int endIndex = threadId * bolckSize-1;//线程下载的结束位置
if(threadId == threadTotal){//如果是最后一个线程,就直接下载到文件尾部
endIndex = dataLength;
}
//调用线程执行下载
new DownloadThread(urlStr,threadId, startIndex, endIndex,obj).start();
}
}else{
System.out.println("链接失败");
}
}

public class DownloadThread extends Thread{
/**线程ID*/
private int threadId;
/**线程开始下载位置*/
private int startIndex;
/**线程结束下载位置*/
private int endIndex;
/**线程下载文件路径*/
private String path;
/**线程ID*/
private Object o;
public DownloadThread(String path,int threadId, int startIndex, int endIndex,Object o) {
this.path = path;
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
this.o = o;
}

@Override
public void run() {
try {
// TODO Auto-generated method stub
File file = new File("/sdcard/"+threadId+".txt");
int downloadTotal = 0;
//检查是否有下载记录,如果有记录,这初始化线程的下载位置
if(file.exists() && file.length() > 0){
FileInputStream fis = new FileInputStream(file);
byte[] bt = new byte[1024];
int length = fis.read(bt);
String downloadStr = new String(bt,0,length);
downloadTotal = Integer.parseInt(downloadStr);
startIndex += downloadTotal;
synchronized (o) {
downTotal += downloadTotal;
}
System.out.println("线程:"+threadId+" 继续开始下载:"+startIndex);
}
URL url = new URL(urlStr);
HttpURLConnection  con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
//请求服务器下载部分资源,指定文件位置
con.setRequestProperty("Range","bytes=" + startIndex + "-"+endIndex);
con.setConnectTimeout(5000);
//得到服务器返回码,200是全部资料请求成功,206是部分资源请求成功
int code = con.getResponseCode();
if(code == 206){
InputStream is = con.getInputStream();
RandomAccessFile raf = new RandomAccessFile(savePath,"rwd");
raf.seek(startIndex);
if(is != null){
byte [] data = new byte[1024];
int len = 0;
while((len = is.read(data)) != -1){
//生成下载进度文件,保存下载进度,实现断点下载
RandomAccessFile info = new RandomAccessFile("/sdcard/"+threadId+".txt","rwd");
raf.write(data, 0, len);
downloadTotal += len;
info.write((""+downloadTotal).getBytes());
info.close();
//同步块为了防止线程修改下载总量的安全问题
synchronized (o) {
downTotal += len;
bar.setProgress(downTotal);
System.out.println("下载进度:"+(downTotal*100/totals));
Message message = new Message();
message.what = MainActivity.JD;
Bundle b = new Bundle();
b.putString("jd", (downTotal*100/totals)+"");
message.setData(b);
((MainActivity)o).handler.sendMessage(message);
}
}
raf.close();
is.close();
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
System.out.println("线程:"+threadId+" 下载结束");
//判断是否下载完毕,如果下载完毕,则删除下载记录文件
if(downTotal == totals){
for(int i = 1; i <= threadTotal;i++){
File file = new File("/sdcard/"+i+".txt");
file.delete();
}
Message message = new Message();
message.what = MainActivity.SUCCESS;
((MainActivity)o).handler.sendMessage(message);
System.out.println("线程:"+threadId+" 下载结束,清除文件成功!");
}
}

}
}

}

<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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="http://192.168.2.101:8080/QQ.exe"
android:ems="10" />
<ProgressBar
android:id="@+id/pb"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="下载进度:" />
<Button
android:id="@+id/button1"
android:onClick="click"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button" />

</LinearLayout>

点击下载源码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 多线程