android多线程访问网络,Android Day05-网络编程之文件下载之多线程断点续传技术

文件下载之多线程断点续传技术底层实现

通过HttpURLConnection连接

断点续传核心步骤:

1.UI设计

2.获取服务器文件的大小,通过连接的一个方法getContentLength()来得到。

3.在客户端创建一个和将要下载的文件的同样大小的同名文件。

4.计算每个线程的起始位置和结束位置

5.开启多个线程,采用RandomAccessFile类,根据计算得到的起始位置和结束位置来随机的读取服务

器资源的输出流,并且实时的采用RandomAccessFile类保存线程读取到的字节位置。

6.判断线程结束和文件上传成功。

断点续传具体实现:

1.UI设计

下载主界面设计

9db722aa01277b08fa8fad204d3268ae.png

进度条单独存放在一个布局文件中,注意进度条要采用以下的样式的进度条。

6cf52564478a07e51d7f46a64d77c90b.png

2.代码逻辑

多线程断点续传的技术可以说是目前学Android课程以来最难、代码最长的一个案例,那么如何

有章有序的将代码写出来呢?

思路整理:

1)网络访问权限和SD卡访问权限的添加

2)初始化动作及下载点击事件的书写9b28bd5e6d2b5be494c706fd7563f520.png

3)随机读取文件线程类的定义

81cf94bd539e1415f9863757277d04a3.png

具体代码:// 注册点击事件

public void click(View v) {

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

String threadcount_str = et_threadcount.getText().toString().trim();

final int threadcount = Integer.parseInt(threadcount_str);

if (TextUtils.isEmpty(path) || threadcount 

Toast.makeText(getApplicationContext(), "输入错误", 0).show();

return;

}

list_pbBars = new ArrayList();

// 先清空进度条布局

ll_layout.removeAllViews();

// 添加进度条

for (int i = 0; i 

//得到进度条的View对象

View item_pb_View = View.inflate(getApplicationContext(),R.layout.item_progress, null);

//通过View再得到进度条

ProgressBar pb_bar = (ProgressBar) item_pb_View.findViewById(R.id.item_pb);

list_pbBars.add(pb_bar);

ll_layout.addView(item_pb_View);

}

try {

//将服务器文件路径用File包装一下,以便获得它的文件名。

sourceFile = new File(path);

//访问URL得到文件的大小 ,通过Content-Length得到。

new Thread(){

public void run()

{

System.out.println("xxx"+Thread.currentThread().getName());

try {

//创建HttpURLConnection

URL url = new URL(path);

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

conn.setRequestMethod("GET");

//获取状态码

int responseCode = conn.getResponseCode();

if(responseCode == 200)

{

//获取资源文件的长度

sourceFileLength = conn.getContentLength();

sourceFileName = sourceFile.getName();

clientFile = new RandomAccessFile(Environment.getExternalStorageDirectory() + "/"

+ sourceFileName, "rw");

clientFile.setLength(sourceFileLength);

//正在运行的线程

runningThreadCount = threadcount;

// 第3步: 计算每个线程读取文件的起始和结束位置

int blockSize = sourceFileLength / threadcount;

for (int i = 0; i 

//得到每个线程读取的起始和结束位置

int start_index = i * blockSize;

int end_index = (i + 1) * blockSize - 1;

int last_index = -1;

if(i == threadcount - 1)

{

end_index = sourceFileLength -1;

}

//------如果采用了断点续传,线程的起始位置就是上次记录位置。

File recordIndexFile = new File(Environment.getExternalStorageDirectory().getPath() + "/" +i + ".txt");

//如果有断点文件,就得到上次记录的读取位置。

if(recordIndexFile.exists() && recordIndexFile.length() > 0)

{

BufferedReader bufr = new BufferedReader(new FileReader(recordIndexFile));

last_index = Integer.parseInt(bufr.readLine());

//last_index++;

bufr.close();

}

else  //否则上次读取位置就是开始位置。

{

last_index = start_index;

}

//设置进度条的总长度

list_pbBars.get(i).setMax((int)(end_index - start_index));

System.out.println("线程"+i +"::"+ last_index + "----" + end_index);

// 第4步:启动线程

new DownloadThread(i, start_index, end_index,last_index).start();

}

}

else

{

showToast("请求不到资源 ");

return;

}

} catch (Exception e) {

e.printStackTrace();

}

}

}.start();

// 第5步:判断线程结束

} catch (Exception e) {

e.printStackTrace();

}

}

// 定义下载的线程类

class DownloadThread extends Thread {

private int threadID;

private int  start_index, end_index,last_index;

private int total = 0;//记录已经读写的总的字节数

public DownloadThread(int threadID, int start_index, int end_index,int last_index) {

this.threadID = threadID;

this.start_index = start_index;

this.end_index = end_index;

this.last_index = last_index;

this.setName("线程" + threadID);

}

public void run() {

try {

//创建HttpURLConnection

URL url = new URL(path);

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

conn.setRequestMethod("GET");

//conn.setReadTimeout(5);

//设置一个头信息Range,表示要读取的流的字节范围。

conn.setRequestProperty("Range", "bytes=" + last_index + "-" + end_index);

//获取状态码

int responseCode = conn.getResponseCode();

//注意多线程下载状态得用206来判断

if(responseCode == 206)

{

//得到服务器的输出流

InputStream serverIn = conn.getInputStream();

//得到客户端的文件输出流

RandomAccessFile myClientFile = new RandomAccessFile(

Environment.getExternalStorageDirectory() + "/"

+ sourceFileName, "rwd");

myClientFile.seek(last_index);

//读取服务器文件写入到客户端文件中

//断点续传文件

int len = 0;

byte[] buf = new byte[1024*1024];

while((len = serverIn.read(buf))!= -1)

{

myClientFile.write(buf,0,len);

total += len;

//实时存储当前读取到的文件位置。

int current_index = last_index + total;

//因为此处实时的存储读取文件的位置,将缓冲区buf稍微弄大点。

// 改

/***注意读取文件位置的流一定要用RandomAccessFile,因为它可以直接同步到缓存(磁盘上)。***/

RandomAccessFile raffAccessFile = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+"/"+threadID+".txt", "rwd");

raffAccessFile.write(String.valueOf(current_index).getBytes());

raffAccessFile.close();

//设置进度条的当前进度

list_pbBars.get(threadID).setProgress((int)(total + last_index - start_index));

}

myClientFile.close();

serverIn.close();

//操作共享资源,注意加锁。

synchronized (DownloadThread.class) {

runningThreadCount--;

if(runningThreadCount == 0)

{

Toast.makeText(getApplicationContext(), "文件下载完毕", 0).show();

}

}

}

else

{

showToast("服务器忙");

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值