首先补充一个知识点,在第一次创建一个类继承Activity的时候,如果这个类中又定义了接口和内部累(包括匿名内部类)。那么这个时候这个在这里面使用this是当前这个对象的,而不是Activity的。如果想展示界面等必须要使用getapplicationcontext等。但是如果是xml布局文件中添加的Button方法,那么这个在Activity子类中实现的方法,不是一个类,而是一个类中的方法,因此这里面是可以使用this的。
多线程断点下载可以分为以下几个步骤:
(1)xml界面设计
(2)通过http请求获取文件大小,并在本地创建一个同样大小的文件。
(3)创建多线程开始下载
(4)读取缓存,查看是否有断点下载
(5)开始下载更新文件
(6)所有下载全部完成之后删除本地缓存记录
(1)
布局如下,注意下面没有显示出来的是一个线性布局,这里可以动态的添加下载的多线程个数。
mLinearLayout.removeAllViews();
for (int i = 0; i < 3; i++) {
ProgressBar mProgressBar = (ProgressBar) View.inflate(this, R.layout.pgb, null);
mLinearLayout.addView(mProgressBar);
mlistbar.add(mProgressBar);
}
(2)在Button的点击响应方法中添加一个子线程
new Thread(new Runnable() {
@Override
public void run() {
try {
Log.i("2", "2");
URL mUrl = new URL(urlteString);
HttpURLConnection mConnection = (HttpURLConnection) mUrl.openConnection();
mConnection.setRequestMethod("GET");
mConnection.setConnectTimeout(10000);
int statecode = mConnection.getResponseCode();
Log.i("456", "456");
if (statecode == 200) {
long filesize = mConnection.getContentLength();//get file size
File mFile = new File(Environment.getExternalStorageDirectory(), getfilenamefromurl(mUrl));
RandomAccessFile mAccessFile = new RandomAccessFile(mFile, "rw");
mAccessFile.setLength(filesize);
long threadblocksize = filesize/Threadnum;
// 启动下载线程
beginthreaddownload(threadblocksize, Threadnum, filesize, mlistbar,mUrl);
}
mConnection.disconnect();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}).start();
这里可以只获取文件的大小:mConnection.getContentLength()另外创建一个分段下载文件的时候:RandomAccessFile 这个文件类型,可以在任意位置对文件进行读写操作,一般用于文件缓存。
(3)根据分块大小等参数创建子线程开始分段下载
private void beginthreaddownload(long blocksize,int threadnum,long size,List<ProgressBar> mList,URL path){
for (int i = 0; i < threadnum; i++) {
long blockbeginbyte = i*blocksize;
long blockendbyte = (i+1)*blocksize-1;
if (i == threadnum - 1) {
blockendbyte = size - 1;
}
mList.get(i).setMax((int) (blockendbyte - blockbeginbyte));
new subdownloadthread(i, blockbeginbyte, blockendbyte, path).start();
Log.i("456", "max"+(blockendbyte - blockbeginbyte)+"end");
}
}
同时设置进度条下载进度的最大数值。(4)检查是否有缓存数据
private class subdownloadthread extends Thread{
private int threadId;
private long startIndex;
private long endIndex;
private URL path;
public subdownloadthread(int threadId, long startIndex, long endIndex,
URL path) {
super();
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
this.path = path;
}
@Override
public void run() {
// TODO Auto-generated method stub
//super.run();
try {
// 检查是否断点下载,更新block的begin end位置
long currentsize = 0;
File mposition = new File(Environment.getExternalStorageDirectory(),getfilenamefromurl(path)+threadId + ".txt");
if (mposition.exists() && mposition.length() > 0) {
FileInputStream mInputStream = new FileInputStream(mposition);
BufferedReader mReader = new BufferedReader(new InputStreamReader(mInputStream));
int blocksizestep = Integer.valueOf(mReader.readLine());
startIndex += blocksizestep;
currentsize += blocksizestep;
mInputStream.close();
}
注意这里子线程的实现不是通过匿名内部类的方法实现的,而是通过创建一个线程类的子类。注意匿名对象要start。(5)开始下载并实时保存下载进度以及更新当前下载进度条
// 建立连接
HttpURLConnection mUrlConnection = (HttpURLConnection) path
.openConnection();
mUrlConnection.setRequestMethod("GET");
mUrlConnection.setRequestProperty("Range", "bytes=" + startIndex + "-"+ endIndex);
mUrlConnection.setConnectTimeout(10000);
int code = mUrlConnection.getResponseCode();
Log.i("123", "xxx"+code);
if (code/100 == 2) {
Log.i("123", "123");
InputStream mInputStream = mUrlConnection.getInputStream();
File file = new File(Environment.getExternalStorageDirectory(),getfilenamefromurl(path));
RandomAccessFile finalfile = new RandomAccessFile(file, "rw");
// 指定文件开始写的位置。
finalfile.seek(startIndex);
int len = 0;
byte buffer[] = new byte[40];
while ((len = mInputStream.read(buffer)) != -1) {
RandomAccessFile filepositon = new RandomAccessFile(mposition,"rwd");
finalfile.write(buffer, 0, len);
currentsize += len;
filepositon.write(String.valueOf(currentsize).getBytes());
filepositon.close();
Log.i("ffff", ""+threadId+"curr"+currentsize);
mlistbar.get(threadId).setProgress((int) currentsize);
}
mInputStream.close();
finalfile.close();
}
(6)下载完成 同步删除本地缓存文件,更新handler
synchronized (MainActivity.class) {
threadactiveacount --;
if (threadactiveacount < 1) {
System.out.println("所有的线程都工作完毕了。删除临时记录的文件");
for (int i = 0; i < 3; i++) {
File f = new File(Environment.getExternalStorageDirectory(),getfilenamefromurl(path)+ i + ".txt");
System.out.println(f.delete());
}
Message msg = Message.obtain();
msg.what = DOWNLOAD_OK;
handler.sendMessage(msg);
}
}
因为所有的线程都是在这个MainActivity中实现的,所以所有的同步方法只要设置唯一的MainActivity.class 即可。