频道栏目
首页 > 资讯 > Android > 正文

[android ,11]11.android多线程断点下载

13-12-25        来源:[db:作者]  
收藏   我要投稿

一、多线程下载原理:

1、在客户端创建出来一个文件,该文件大小与服务器上的文件大小完全性相同。

①、首先要知道服务器上文件的大小,通过相应头里 的content-length得到文件大小。

②、使用RandomAccessFile类随机创建一个文件 ,通过setLength方法设置文件大小。

2、开辟三个线程,进行下载,计算出每一个线程下载的数据块大小。

3、当三个线程都运行完毕后,所有的数据都已经下载好了。

二、javase中的文件下载案例:支持断点下载:

 

public classTestMutileDownload {

//服务器上要下载的资源的地址

public static final String path="http://192.168.1.247:8080/youdao.exe";

public static int threadcount;

 

public static void main(String[] args) throws Exception{

URL url = new URL(path);

//打开连接

HttpURLConnection conn =(HttpURLConnection) url.openConnection();

conn.setRequestMethod("GET");

//设置请求头,User-Agent:表示资源来自哪里

conn.setRequestProperty("User-Agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");

conn.setConnectTimeout(5000);//得到服务器响应状态码的时间

// 获取服务器文件的大小

int len =conn.getContentLength();

System.out.println("文件大小为"+len);

// 在本地创建出来一个大小跟服务器文件一样大的资源

File file = newFile("youdao.exe");

//创建下载的文件对象

RandomAccessFilerandomfile = new RandomAccessFile(file,"rwd");

randomfile.setLength(len);

 

 

 

// 假设只开启3个子线程

int blocksize = len/3;

for(int i =1;i<=3;i++){

threadcount = 0;

int startsize = (i-1)*blocksize;

int endsize = (i)*blocksize - 1;

 

if(i==3){

// if(endsize<=len)

endsize = len;

}

new Thread(new DownLoadTask(i, startsize, endsize)).start();

 

// 怎么才能知道3个子线程都执行完毕了呢?

}

}

}

 

线程类:

class DownLoadTaskimplements Runnable{

//线程的id

private int id;

//要下载的文件的开始位置

private int startsize;

// 要下载的文件的结束位置

private int endsize;

//HttpURLConnection.setRequestProperty("Range","bytes=2097152-4194303");

 

public DownLoadTask(int id, int startsize, int endsize) {

this.id = id;

this.startsize =startsize;

this.endsize = endsize;

}

 

 

 

@Override

public void run() {

try {

// 每一个线程创建执行的时候 都创建一个id.txt的文件,这个文件用来记录当前线程下载的进度

File idfile = new File(id+".txt");

// 判断是否记录的有下载的位置信息

if(idfile.exists()){

FileInputStream fis =new FileInputStream(idfile);

byte[] result =StreamTools.getBytes(fis);

String numberstr = newString(result);

if(numberstr!=null&&(!"".equals(numberstr))){

int startposition = Integer.parseInt( numberstr);

if(startposition>startsize){

startsize =startposition; // 重新指定下载的开始位置

}

 

}

}

 

URL url = new URL(TestMutileDownload.path);

HttpURLConnection conn = (HttpURLConnection)url.openConnection();

conn.setRequestMethod("GET");

conn.setRequestProperty("User-Agent", "Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1; SV1)");

conn.setConnectTimeout(5000); //得到服务器响应状态码的时间

// 指定当前线程从服务器上的哪个位置下载文件y

if(startsize>endsize){

startsize=endsize-1;

}

conn.setRequestProperty("Range","bytes="+startsize+"-"+endsize);

System.out.println("线程id="+id+"开始位置"+startsize+"结束位置"+endsize);

InputStream is = conn.getInputStream();

File file = new File("youdao.exe");

RandomAccessFile randomfile = new RandomAccessFile(file, "rwd");

//指定指针开始的位置

randomfile.seek(startsize);

//randomfile.write(arg0, arg1, arg2);

byte[] buffer = new byte[1024];

int len = 0;

int total = 0;

while( (len = is.read(buffer))!=-1){

randomfile.write(buffer,0, len);

// 记录当前 线程下载的数据量 和对应的位置给记录

total +=len;

FileOutputStream idfos =new FileOutputStream(idfile);

idfos.write((startsize+total+"").getBytes());// 记录当前线程下载的位置信息

idfos.flush();

idfos.close();

}

randomfile.close();

is.close();

System.out.println("线程"+id+"下载完毕");

// //线程下载完毕后 擦屁股的操作

// if(idfile.exists()){

// idfile.delete();

// }

 

synchronized (TestMutileDownload.class) {

TestMutileDownload.threadcount++;

if(TestMutileDownload.threadcount>=3){

System.out.println("所有的线程都执行完毕了");

// 擦屁股的操作

for(int i=1;i<=3;i++){

File deletefile = newFile(i+".txt");

System.out.println(i+"删除"+ deletefile.delete());

}

}

}

 

 

} catch (Exception e) {

e.printStackTrace();

}

 

}

}

三、android的多线程下载:

1、设置布局:

 

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical">

 

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="多线程断点下载器" />

 

android:id="@+id/et_path"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="http://192.168.1.247:8080/youdao.exe"

android:hint="请输入下载文件的路径" />

 

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:gravity="center_horizontal" //水平居中

android:orientation="horizontal" >

 

android:id="@+id/bt_download"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="下载" />

 

android:id="@+id/bt_stop"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="暂停" />

 

 

android:id="@+id/pb"

style="?android:attr/progressBarStyleHorizontal"//进度条的样式.

android:layout_width="fill_parent"

android:layout_height="wrap_content" />

 

android:id="@+id/tv_progress"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="进度" />

 

 

 

2、业务代码:

 

public class DemoActivityextends Activity implements OnClickListener {

protected static final int ERROR = 404;

public static final int DOWNLOAD_FINISH = 200;

private EditText et_path;

private Button bt_download;

private Button bt_stop;

private ProgressBar pb;

private TextView tv_progress;

public static int threadcount ;

public int total; // 当前下载的进度

public int totallen ; // 总的文件大小

 

private Handler handler = new Handler(){

 

@Override

public voidhandleMessage(Message msg) {

// TODO Auto-generated method stub

super.handleMessage(msg);

if(msg.what==ERROR){

Toast.makeText(getApplicationContext(),"获取文件长度失败", 0).show();

bt_download.setClickable(true);

bt_download.setEnabled(true);

return;

}

if(msg.what==DOWNLOAD_FINISH){

bt_download.setClickable(true);

bt_download.setEnabled(true);

return;

}

int process = total*100/totallen;

String strprocess = "当前进度"+process+"%";

tv_progress.setText(strprocess);

}

 

};

 

 

 

public boolean flag;

@Override

public void onCreate(BundlesavedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

et_path = (EditText)this.findViewById(R.id.et_path);

bt_download = (Button)this.findViewById(R.id.bt_download);

bt_stop = (Button)this.findViewById(R.id.bt_stop);

pb = (ProgressBar)this.findViewById(R.id.pb);

tv_progress = (TextView)this.findViewById(R.id.tv_progress);

 

//注册按钮的点击事件

bt_download.setOnClickListener(this);

bt_stop.setOnClickListener(this);

 

}

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.bt_stop:

flag = false;

bt_download.setClickable(true);

bt_download.setEnabled(true);

break;

case R.id.bt_download:

final String path = et_path.getText().toString().trim();

if("".equals(path)){

Toast.makeText(this,"路径不能为空", 1).show();

return ;

}else{

// 开启子线程 连接服务器 获取文件的大小

bt_download.setClickable(false);

bt_download.setEnabled(false);

new Thread(){

public void run() {

try {

total = 0;

flag = true;

URL url = new URL(path);

HttpURLConnection conn = (HttpURLConnection)url.openConnection();

conn.setRequestMethod("GET");

conn.setRequestProperty("User-Agent", "Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1; SV1)");

conn.setConnectTimeout(5000); //得到服务器响应状态码的时间

// 获取服务器文件的大小

totallen = conn.getContentLength();

//设置进度条的最大值

pb.setMax(totallen);

System.out.println("文件大小为"+totallen);

// 在本地创建出来一个大小跟服务器文件一样大的资源

File file = newFile(Environment.getExternalStorageDirectory(),getFileName(path));

RandomAccessFile randomfile = new RandomAccessFile(file, "rwd");

randomfile.setLength(totallen);

 

 

 

// 假设只开启3个子线程

int blocksize = totallen/3;

for(int i = 1;i<=3;i++){

threadcount = 0;

int startsize =(i-1)*blocksize;

int endsize =(i)*blocksize - 1;

if(i==3){

endsize = totallen;

}

 

new Thread(newDownLoadTask(i, startsize, endsize,path)).start();

 

// 怎么才能知道3个子线程都执行完毕了呢?

 

}

} catch (Exception e) {

e.printStackTrace();

// Toast.makeText(this, "下载出错", 0).show();

Message msg = new Message();

msg.what = ERROR;

handler.sendMessage(msg);

}

};

}.start();

 

}

 

 

break;

}

 

}

 

class DownLoadTask implements Runnable{

//线程的id

private int id;

//要下载的文件的开始位置

private int startsize;

// 要下载的文件的结束位置

private int endsize;

//HttpURLConnection.setRequestProperty("Range","bytes=2097152-4194303");

private String path;

 

public DownLoadTask(intid, int startsize, int endsize,String path) {

this.id = id;

this.startsize = startsize;

this.endsize = endsize;

this.path = path;

}

 

 

 

@Override

public void run() {

try {

// 每一个线程创建执行的时候 都创建一个id.txt的文件,这个文件用来记录当前线程下载的进度

File idfile = newFile("/mnt/sdcard/"+id+".txt");

// 判断是否记录的有下载的位置信息

if(idfile.exists()){

FileInputStream fis = new FileInputStream(idfile);

byte[] result = StreamTools.getBytes(fis);

String numberstr = new String(result);

if(numberstr!=null&&(!"".equals(numberstr))){

int startposition =Integer.parseInt( numberstr); // 从文件里面获取到的位置信息

if(startposition>startsize){

int currentposition = startposition - startsize; // 当前线程已经下载的数据的大小

setProgreebarProgress(currentposition);

 

handler.sendEmptyMessage(0);

// handler.post(r);

startsize = startposition; // 重新指定下载的开始位置

}

 

}

}

 

URL url = new URL(path);

HttpURLConnection conn =(HttpURLConnection) url.openConnection();

conn.setRequestMethod("GET");

conn.setRequestProperty("User-Agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");

conn.setConnectTimeout(5000);//得到服务器响应状态码的时间

// 指定当前线程从服务器上的哪个位置下载文件y

if(startsize>endsize){

startsize=endsize-1;

}

conn.setRequestProperty("Range","bytes="+startsize+"-"+endsize);

System.out.println("线程id="+id+"开始位置"+startsize+"结束位置"+endsize);

InputStream is =conn.getInputStream();

File file = newFile(Environment.getExternalStorageDirectory(),getFileName(path));

RandomAccessFilerandomfile = new RandomAccessFile(file,"rwd");

randomfile.seek(startsize);

//randomfile.write(arg0,arg1, arg2);

byte[] buffer = newbyte[1024];

int len = 0;

int total = 0;

while( (len =is.read(buffer))!=-1){

 

randomfile.write(buffer, 0, len);

// 记录当前 线程下载的数据量 和对应的位置给记录

total +=len;

setProgreebarProgress(len);

handler.sendEmptyMessage(0);

FileOutputStream idfos = new FileOutputStream(idfile);

idfos.write((startsize+total+"").getBytes()); // 记录当前线程下载的位置信息

idfos.flush();

idfos.close();

if(!flag){

return ;

}

 

}

randomfile.close();

is.close();

System.out.println("线程"+id+"下载完毕");

// //线程下载完毕后 擦屁股的操作

// if(idfile.exists()){

// idfile.delete();

// }

 

synchronized (DemoActivity.this){

threadcount ++;

if(threadcount>=3){

System.out.println("所有的线程都执行完毕了");

// 擦屁股的操作

for(inti=1;i<=3;i++){

File deletefile = newFile("/mnt/sdcard/"+i+".txt");

System.out.println(i+"删除"+ deletefile.delete());

}

//通知主线程 bt_download.setClickable(true);

//bt_download.setEnabled(true);

Message msg = newMessage();

msg.what =DOWNLOAD_FINISH;

handler.sendMessage(msg);

}

}

 

 

} catch (Exception e) {

e.printStackTrace();

}

 

}

}

 

private synchronized void setProgreebarProgress(int len){

total += len;

pb.setProgress(total);

// 第二种做法 就是每次得到下载的进度后 把数据存到文件里面

}

 

 

public String getFileName(String path){

int start =path.lastIndexOf("/")+1;

returnpath.substring(start);

 

}

}

 

只有创建View对象的线程,才可以更新view对象里的内容,

其实所有的view对象都是在主线程里面创建的 ,线程的名字叫main。

所用的与ui相关的界面都是在主线程里面创建的。

 

更新view显示基本原理:

当子线程要更新view里的内容时,就让子线程发送一个消息给主线程,主线程再根据消息的内容进行操作。

主线程里面有消息队列(message queue),可以存放一组消息。并且还有一个轮询器,定期的轮询消息队列,查看是否有消息。如果发现有消息,;轮询器会把消息取出来。

在主线程中创建消息的处理者handler对象,用于处理在消息队列中取出的消息。

相关TAG标签
上一篇:iOS JSONKit的一些常用方法记录
下一篇:进程与内存7-高速缓存2(目录项高速缓存、索引节点高速缓存和磁盘高速缓存)
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站