背景
- 我觉得现在出生的码农是幸福的,为什么这么说呢,因为android2.0时代是一去不复返,那时候网上没有第三方的框架,网上没有抄袭,everything都是靠自己写,靠自己对着长长的源码瞅原理,对于眼睛小的人,瞬间都是1000000点伤害啊,想想都有吐血的感觉,但是现在有很多第三方的东西,用起来很也爽,所以我要偷偷的告诉大家,有时候庆幸我可以比老程序员多撩几年妹,哈哈哈。。那小伙子们在爽的同时,是不是也该了解其原理呢,接下来带大家看看《多线程下载》原理!!!!
效果图先献给各位再讨论说话![这里写图片描述]
步骤
- (1)确定线程线程
//设置线程的数量
public int threadCount = 3;`
- (2)通过链接访问服务器,通过链接获取到文件的总大小,然后平分给3个线程小弟(注意:有时候不能平分,就让最后那个小弟多挡当一点) 如下图:
看完图了之后,我相信懂了大致原理,我现在上代码:
//防止主线程堵塞,我们一般创建子线程处理:
//恢复下载
isLoad = true;
new Thread(new Runnable() {
@Override
public void run() {
try {
// ①获取图片的总大小,计算每个线程应承当的大小
URL url = new URL(filePath);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
Log.i("TAG", "响应码::" + connection.getResponseCode());
if (connection.getResponseCode() == 200) {
//得到文件大小
allLength = connection.getContentLength();
progressBar.setMax(allLength);
//②生成临时文件,并设置临时文件的大小
//这里为什么要用RandomAccessFile,而不是FileOutput,原因是RandomAccessFile有个
//Random单词,随机的意思,就意味着我们更加的灵活的读写,详细我就不多啰嗦了,在网上可以查看
File file = new File(Environment.getExternalStorageDirectory(), fileName);
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.setLength(allLength);
raf.close();
//现在就给线程小弟分任务了
int averAge = allLength / threadCount;
for (int i = 0; i < threadCount; i++) {
startPostion = averAge * i;
if (i == threadCount - 1) {
endPositon = allLength - 1;
} else {
endPositon = (i + 1) * averAge - 1;
}
new MyThread(startPostion, endPositon, i).start();
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
- (3)每个子线程设置好下载区间,再一次网络请求,并且要把每一次暂停之后,每个线程下载了多少,要记录下来,在一次点击下载时,并获取出来。
//获取暂停每个子线程下载的大小
File file = new File(Environment.getExternalStorageDirectory(), threadId + ".text");
//获取暂停以后,每个线程下载的大小
if (file.exists()) {
FileInputStream inputStream = new FileInputStream(file);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
int newStart = Integer.parseInt(reader.readLine());
startPostion += newStart;
}
Log.i(TAG, "线程" + threadId + "小弟开始下载位置是" + startPostion);
Log.i(TAG, "线程" + threadId + "小弟结束下载位置是" + endPostion);
URL url = new URL(filePath);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setReadTimeout(5000);
connection.setConnectTimeout(5000);
//设置本次http请求所请求的数据的区间
connection.setRequestProperty("Range", "bytes=" + startPostion + "-" + endPostion);
if (connection.getResponseCode() == 206) {
InputStream inputStream = connection.getInputStream();
Log.i(TAG, "线程" + threadId + "要下载的总大小::" + connection.getContentLength());
File f = new File(Environment.getExternalStorageDirectory(), fileName);
RandomAccessFile randomAccessFile = new RandomAccessFile(f, "rwd");
randomAccessFile.seek(startPostion);
byte[] b = new byte[1024];
int len = 0;
//记录每个线程下载的大小
int total = 0;
while ((len = inputStream.read(b)) != -1) {
randomAccessFile.write(b, 0, len);
total += len;
Log.i(TAG, "线程小弟" + threadId + "已经帮你下载了::" + total);
allCount += len;
progressBar.setProgress(allCount);
int processCount = progressBar.getProgress()* 100/progressBar.getMax()*100;
Log.e(TAG,"processCount::"+processCount);
//每两个点更新一次 进度条
if (processCount%2==0){
handler.sendEmptyMessage(0);
}
Log.i(TAG, "所有线程下载总量::" + allCount);
//不要好奇为什么可以在子线程里面更新UI,那是因为android把progprogress设计就计算加载进度的
RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
accessFile.write((total + "").getBytes());
accessFile.close();
if(!isLoad){
return;
}
}
Log.i(TAG, "线程" + threadId + "已经下载完毕::总大小为:::" + total);
randomAccessFile.close();
loadCount++;
synchronized (filePath) {
if (loadCount == threadCount) {
allCount = 0;
for (int i = 0; i < threadCount; i++) {
File file1 = new File(Environment.getExternalStorageDirectory(), i + ".text");
if (file1.exists()) {
file1.delete();
}
loadCount = 0;
}
}
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
- (4) 当然了,我们的需求不紧紧只能下载,有时候手贱不小心点击错了,我们得暂停,所以在这里设置一个变量: boolean isLoad ;
- 暂定代码:
/**
* 停止下载
* @param view
*/
public void stopLoad(View view){
isLoad = false;
}
//这里检测到标记就会结束
if(!isLoad){
return;
}
最后祝福几句
- 这里只是告诉大家下载的原理,在实际开发中,我们是要经过调接口的,接口会直接返回文件大小,名称等,所以我们得把这些数据用HashMap,存储起来,记录下载的大小,和暂定时记录已经下载量,还有我这里是直接写在Activity中,建议大家把代码封装到Service里面。
随便把xml布局献上:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:gravity="center"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="30dp"
android:lines="1"
android:onClick="startLoad"
android:text="开始下载"
android:textSize="23sp" />
<Button
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_marginTop="30dp"
android:lines="1"
android:onClick="stopLoad"
android:text="暂停下载"
android:textSize="23sp" />
<ProgressBar
android:id="@+id/pb_load_count"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="15dp"
android:max="100"
android:progress="0"
android:progressDrawable="@drawable/processbackg"
android:visibility="visible" />
<TextView
android:id="@+id/tv_load_count"
android:layout_marginTop="15dp"
android:textSize="26sp"
android:textColor="#ff0000"
android:text="0%"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
//变量名称
private String TAG = "MainActivitya";
//下载路径 我直接找了一个地址,写死了
private static String filePath = "http://gdown.baidu.com/data/wisegame/91319a5a1dfae322/baidu_16785426.apk";
//文件名
private String fileName = "baidu_16785426.apk";
//设置线程的数量
public int threadCount = 3;
//开始位置
public int startPostion;
//结束位置
public int endPositon;
//记录所有线程整个文件大小
public int allCount;
//记录下载完线程的数量
public int loadCount = 0;
//是否继续下载
public boolean isLoad = true;
//下载精度条
public ProgressBar progressBar;
//下载进度
public TextView tvProcess;
//总长度
public int allLength;