多线程下载
Activity
- 多线程下载
- 多线程下载:
平分服务器中的文件,如果平分不了,最后多余的字节给最后一个线程下载。每个线程只下载自己所对应的服务器资源,使用RandomAccessFile下载到本地指定位置,最后所有线程都下载完了该文件就是一个完整的文件。
多线程下载不会超过单线程下载速度,不会超过最大带宽
- 多线程下载:
- 多线程下载步骤
- 开启网络获取服务器文件的总长度 totalSize;
int totalSize = new URL("http://192.168.82.52:8080/Day08/Deep.mp3")
.openConnection().getContentLength();
- 确定要开启几个线程,一般线程个数是CPU核数 + 1
int threadCount = Runtime.getRuntime().availableProcessors();
- 计算每个线程下载的范围 [startIndex , endIndex];平均字节:avgSize,
int avgSize = totalSize / threadCount;
for (int i = 1; i <= threadCount; i++) {
int startIndex = avgSize * (i - 1);
int endIndex = avgSize * i - 1;
if (i == 3) {
endIndex = totalSize - 1;
}
// 5创建子线程对象
MyThread mThread = new MyThread(i, startIndex, endIndex,
"http://192.168.82.52:8080/Day08/Deep.mp3",
"D:\\ceshi\\Deep.mp3");
mThread.start();
}
}
- 自定义线程类,然后创建各个线程,在线程的构造函数中传入参数
static class MyThread extends Thread {
private int id;//线程名
private int startIndex;//开始的索引
private int endIndex;//结束的索引
private String urlpath;//下载的地址
private String targePath;//下载的位置以及文件名
// 创建一个构造方法,来创建线程
public MyThread(int id, int startIndex, int endIndex, String urlpath,
String targePath) {
this.id = id;
this.startIndex = startIndex;
this.endIndex = endIndex;
this.urlpath = urlpath;
this.targePath = targePath;
}
开启各个子线程开始下载各自的任务–>3.5
让各个子线程将字节写入各自的范围
具体步骤:- 获取网络请求
HttpURLConnection httpURLConnection = (HttpURLConnection) new URL(urlpath).openConnection();
设置请求部分属性setRequestProperty(“range”,”bytes”+start+”-“+end);状态码==206
httpURLConnection.setRequestProperty("Range", "bytes="+ startIndex + "-" + endIndex);
判断状态码是否==206
是:获取服务器返回的流getInputstream
InputStream inputStream = httpURLConnection.getInputStream();
- RandomAccessFile写文件(即可度又可写),如果文件不存在就创建一个即可读又可写的文件(文件路径,属性rw)
RandomAccessFile rFile = new RandomAccessFile(targePath,"rw");
- 默认情况下,rFile 指向文件第0个角标,需要移动指针到指定位置,调用.seek(设置的角标值)
rFile.seek(startIndex);
- 创建一个字节数据写入指定文件
int len; byte[] arr = new byte[1024]; while ((len = inputStream.read(arr)) != -1) { rFile.write(arr, 0, len); } System.out.println("线程" + id +"传了"+(endIndex-startIndex)+ " 完成了"); // 释放资源 inputStream.close(); rFile.close();
- 否:返回状态码不对
- 获取网络请求
多线程断点下载
跟多线程下载步骤一样,只是在子线程的构造方法中添加了临时文件的开始标签newStartIndex,和临时记录线程下载的字节数据的文件temFile
//增加的属性
private int newStartIndex;//临时文件的开始标签
private File temFile;//临时文件
//在构造方法中把字节写入临时文件,每次断点后都会从新标签开始写
temFile = new File("D:\\ceshi\\"+id+".txt");
try {
BufferedReader br = new BufferedReader(new FileReader(temFile));
String string = br.readLine();
newStartIndex = Integer.parseInt(string);//获取断点读取的字节数,断点新开始的索引
br.close();
} catch (Exception e) {
e.printStackTrace();
}
//在run方法中读取临时文件存的数据,开始写文件,直到文件下载完成
//注意请求网络时,设置请求部分属性setRequestProperty("Range","bytes"+newstartINdex+"-"+end),是 新书签newstartINdex - 最后的索引,不是开始索引
connection.setRequestProperty("Range", "bytes="+newStartIndex+"-"+endIndex);
while((len=inputStream.read(arr))!=-1){
rFile.write(arr,0,len);
total+=len;
FileWriter fw = new FileWriter(temFile);
fw.write(newStartIndex+total+"");//写的是开始的坐标加新获取的数据
fw.close();
// 打印百分比
// (newStartIndex-startIndex之前下载的字节数+1+total//新下载的字节数???)/endIndex - startIndex +1//总共下载的字节数)
float percent = (newStartIndex-startIndex+1+total+0.f)/(endIndex - startIndex +1);
}
System.out.println("完成了"+this);
inputStream.close();
rFile.close();
}
RandomAcessFile可以进行读和写
- 方法:
- .seek();设置读写开始的索引
- .write():跟普通流流一样可以进行写
- .close();用完要释放资源
xUtils介绍
四大模块:
- DbUtils模块:操作数据库。一行代码进行增删改查
- orm: object relation mapping:对象关系映射
- ViewUtils模块:资源和事件的绑定。一行代码进行findViewById
- IOC:控制反转框架(依赖注入)
- HttpUtils模块:网络请求
- BitmapUtils模块:图片加载
xUtils练习
面试题:
Activity可以传那些数据?
- 8种基本数据类型(以及数组形式)
- String类型
- 实现序列化的接口
什么是Activity?
Activity是Android中四大组件之一,提供了窗口供用户与手机进行交互,比如拨打电话/发短信/照相/浏览网页,只要是手机屏幕上能看到的东西都依托于Activity。(一个界面)
问题
多线程下载,循环子线程时,for的判断条件是i<=线程的个数,int i= 1 ;一般都是从1开始,计算公式的时候相匹配
thread1 = [3 * 0 , 3 - 1]; thread2 = [3 * 1 , 2*3-1]; thread3 = [3 * 2 , 3*3-1]; threadn = [avgSize * (n-1) , avgSize*n-1] if(n == 3){ threadn = [avgSize*(n-1),totalSize-1] }
理不清RandomAccessFile(),用的时候用成,———–在写数据是用 RandomAccessFile() 来写数据
- 获取endIndex时,当是最后一个线程时 endIndex= 文件总长度-1;
- 获取网络请求时,用的是HttpURLConnection,容易导错包
在断点下载时,设置请求部分属性时,是新书签,而不是开始书签
connection.setRequestProperty("Range", "bytes="+newStartIndex+"-"+endIndex);
断点下载时.断点后开始写入的是新书签加上在下次断点前读取的数据
fw.write(newStartIndex+total+"");
不明白设置请求部分请求为什么那样写?