今天来记录一下安卓的多线程下载。
先来说一下整体的一个思路:因为是要去下载目标文件,那么我们就需要先使用目标下载url来获得目标文件的大小,以及文件名(以便在本地创建该文件名)。然后开始创建线程,创建线程使用for循环,相信大家都懂。对于创建的线程,我们可以写具体的方法,来实现相应的下载任务。
首先创建一个emptyAvtivity。
由于初学者对多线程机制不了解,所以不推荐直接在项目中写代码。在File中New Module
在弹出对话框选择,JAVA Library,看图。
点击Next,为我们的项目重命名一下,命名为MultiDownloader
点击Finish完成创建。此时创建的类是在LIb目录下,具体看图
要完成下载,需要新建一个main方法。具体代码如下。。。
package com.glsite.lib;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class MultiDownloader {
/**
* 总共的线程数
*/
public static final int TOTAL_THREAD_COUNT = 3;
/**
* 要下载的连接
*/
public static String path = "http://localhost:8080/Day10/qq.exe";
/**
* 运行状态下的线程数
*/
private static int runningThreadCount = 0;
public static void main(String[] args) {
try {
URL url = new URL(path);
HttpURLConnection coon = (HttpURLConnection) url.openConnection();
//请求方式是GET
coon.setRequestMethod("GET");
//获取响应码
int code = coon.getResponseCode();
//对响应码进行判断
if (code == 200) {
//如果响应码是200,那么返回目标的长度(大小)
int length = coon.getContentLength();
System.out.println("file length:" + length);
//对下载的文件进行命名,命名的规则写了一个getDownloadFileName方法
RandomAccessFile raf = new RandomAccessFile(getDownloadFileName(path), "rw");
//我们在本地新建一个和目标文件大小一样的文件,用过迅雷的都知道吧
raf.setLength(length);
raf.close();
//设置每个线程下载的大小,其中TOTAL_THREAD_COUNT表示分成的线程数
int blockSize = length / TOTAL_THREAD_COUNT;
System.out.println("every block size:" + blockSize);
runningThreadCount = TOTAL_THREAD_COUNT;
//开始创建线程
for (int threadID = 0; threadID < TOTAL_THREAD_COUNT; threadID++) {
int startPosition = threadID * blockSize;
//下面公式最后-1是因为,blocksize是从1开始,而文件下载时从0开始,所以要减一
int endPosition = (threadID + 1) * blockSize - 1;
if (threadID == (TOTAL_THREAD_COUNT - 1)) {
endPosition = length - 1 ;
}
//threadID, startPosition, endPosition分别表示线程的id,该线程的起始位置,该线程的结束位置
new DownloadThread(threadID, startPosition, endPosition).start();
}
} else {
System.out.println("download error, code = " + code);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 从网络下载路径获取文件名
* @param path
* 网络路径
* @return 文件名
*/
private static String getDownloadFileName(String path) {
//获取最后一个/后的文件进行命名
return path.substring(path.lastIndexOf("/") + 1);
}
private static class DownloadThread extends Thread{
/**
* 当前线程ID
*/
private int threadID;
/**
*当前线程起始位置
*/
private int startPosition;
/**
*当前线程结束位置
*/
private int endPosition;
public DownloadThread(int threadID, int startPosition, int endPosition) {
this.threadID = threadID;
this.startPosition = startPosition;
this.endPosition = endPosition;
}
@Override
public void run() {
System.out.println("thread:" + threadID + "beginworking");
try {
File finfo = new File(TOTAL_THREAD_COUNT + getDownloadFileName(path) + threadID + ".txt");
if (finfo.exists() && finfo.length() > 0) {
FileInputStream fis = new FileInputStream(finfo);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String lastposition = br.readLine();
startPosition = Integer.parseInt(lastposition);
fis.close();
}
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
System.out.println("begin and end:" + threadID + "range of download:" + startPosition + "~~" + endPosition);
//下面的s1中的-表示谁到谁的意思
conn.setRequestProperty("Range","bytes=" + startPosition + "-" + endPosition);
int code = conn.getResponseCode();
//这里的code是206,不要有疑问,因为是分段下载,所以是206,哈哈哈,不过我暂时也不知道为啥,可能是规则吧。
if (code == 206) {
//如果响应码是206,那么就要开始下载了,传入一个输入流
InputStream is = conn.getInputStream();
RandomAccessFile raf = new RandomAccessFile(getDownloadFileName(path), "rw");
//从起始位置开始
raf.seek(startPosition);
//将流输入写入
int len = 0;
// 这个buffer的byte一般写1024,至于为啥,我也不知道
byte[] buffer = new byte[1024];
int total = 0;//downloaded data of current thread in this time
while ((len = is.read(buffer))!= -1) {
raf.write(buffer,0,len);
total += len;
RandomAccessFile inforraf = new RandomAccessFile(TOTAL_THREAD_COUNT + getDownloadFileName(path) + threadID + ".txt", "rwd");
inforraf.write(String.valueOf(startPosition + total).getBytes());
inforraf.close();
}
is.close();
raf.close();
System.out.println("thread:" + threadID + "download complete...");
} else {
System.out.println("request download failed" );
}
} catch (Exception e) {
e.printStackTrace();
}finally {
synchronized (MultiDownloader.class) {
runningThreadCount--;
if (runningThreadCount <= 0) {
System.out.println("multi thread download complete.");
for (int i = 0; i < TOTAL_THREAD_COUNT; i++) {
File finfo = new File(TOTAL_THREAD_COUNT + getDownloadFileName(path) + i + ".txt");
System.out.println(finfo.delete());
}
}
}
}
}
}
}
以上就是JAVA的多线程下载了,支持断点续传奥。下面我们就开始将它移植到我们的安卓app上面。
-------------------------我是华丽的分割线-----------------------------------------------------------------
先上一张,总体布局图
将mainactivity的代码改成下面这样就完事了
package com.glsite.multidownloader;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText mEtPath;
private EditText mEtThreadCount;
private LinearLayout mLLContainer;
private Button mBtSelf;
private Button mBtOther;
/**
* 总共的线程数
*/
public int totalThreadCount = 3;
/**
* 要下载的文件的链接
*/
public String path = "http://192.168.1.130:8080/Day10/QQ.exe";
/**
* 运行状态的线程数
*/
private static int runningThreadCount = 0;
/**
* ProgressBar的集合
*/
private ArrayList<ProgressBar> mPbs;
/**
* 当前app的缓存目录
*/
private String CACHE_DIR;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1.找到控件
// 2.在button点击事件当中下载文件
// 3.下载的时候要在界面显示下载的进度
mEtPath = findViewById(R.id.et_path);
mEtThreadCount = findViewById(R.id.et_threadcount);
mLLContainer = findViewById(R.id.ll_container);
mBtSelf = findViewById(R.id.bt_self);
mBtOther = findViewById(R.id.bt_other);
mBtSelf.setOnClickListener(this);
mBtOther.setOnClickListener(this);
// 初始化缓存目录路径
CACHE_DIR = this.getCacheDir().getAbsolutePath() + "/";
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_self:
downloadBySelf();
break;
case R.id.bt_other:
break;
}
}
/**
* 通过自己自定义的多线程代码去下载网络文件
*/
private void downloadBySelf() {
path = mEtPath.getText().toString().trim();
totalThreadCount = Integer.valueOf(mEtThreadCount.getText().toString().trim());
mLLContainer.removeAllViews();
mPbs = new ArrayList<>();
for (int i = 0; i < totalThreadCount; i++) {
// 有几个线程就添加几个progressbar
ProgressBar pb = (ProgressBar) View.inflate(this, R.layout.pb, null);
mLLContainer.addView(pb);
mPbs.add(pb);
}
new Thread() {
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if (code == 200) {
int length = conn.getContentLength();
System.out.println("file length:" + length);
RandomAccessFile raf = new RandomAccessFile(CACHE_DIR + getDownloadFileName(path), "rw");
// 创建一个空的文件并且设置它的文件长度等于服务器上的文件长度
raf.setLength(length);
raf.close();
int blockSize = length / totalThreadCount;
System.out.println("every block size:" + blockSize);
runningThreadCount = totalThreadCount;
for (int threadId = 0; threadId < totalThreadCount; threadId++) {
int startPosition = threadId * blockSize;
int endPosition = (threadId + 1) * blockSize -1;
if (threadId == (totalThreadCount - 1 )) {
endPosition = length - 1;
}
new DownloadThread(threadId, startPosition, endPosition).start();
}
} else {
System.out.println("download error, code = " + code);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
/**
* 从网络路径获取文件名
*
* @param path
* 网络路径
* @return 文件名
*/
private static String getDownloadFileName(String path) {
return path.substring(path.lastIndexOf("/") + 1);
}
/**
* 下载文件的线程
*/
private class DownloadThread extends Thread{
/**
* 线程id
*/
private int threadId;
/**
* 当前现成下载的起始位置
*/
private int startPosition;
/**
* 当前线程下载的终止位置
*/
private int endPosition;
/**
* 当前线程需要去下载的总共的字节
*/
private int threadTotal;
/**
* 该线程上一次下载了的字节数
*/
private int lastDownloadTotalSize;
public DownloadThread(int threadId, int startPosition, int endPosition) {
this.threadId = threadId;
this.startPosition = startPosition;
this.endPosition = endPosition;
this.threadTotal = endPosition - startPosition;
mPbs.get(threadId).setMax(threadTotal);
}
@Override
public void run() {
System.out.println("thread:" + threadId + "begin working");
try {
File finfo = new File(CACHE_DIR + totalThreadCount + getDownloadFileName(path) + threadId + ".txt");
if (finfo.exists() && finfo.length() > 0) {
FileInputStream fis = new FileInputStream(finfo);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String lastPosition = br.readLine();
// 这里计算出来的就是表示的上次该线程下载了多少个字节总数
lastDownloadTotalSize = Integer.parseInt(lastPosition) - startPosition;
startPosition = Integer.parseInt(lastPosition);
fis.close();
}
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
System.out.println("begin and end:" + threadId + " range of download: " + startPosition + "~~" + endPosition);
conn.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
int code = conn.getResponseCode();
if (code == 206) {
InputStream is = conn.getInputStream();
RandomAccessFile raf = new RandomAccessFile(CACHE_DIR + getDownloadFileName(path), "rw");
raf.seek(startPosition);
int len = 0;
byte[] buffer = new byte[1024];
int total = 0; // downloaded data of current thread in this times;
while ((len = is.read(buffer)) != -1) {
raf.write(buffer, 0, len);
total += len;
RandomAccessFile inforaf = new RandomAccessFile(CACHE_DIR + totalThreadCount + getDownloadFileName(path) + threadId + ".txt", "rwd");
inforaf.write(String.valueOf(startPosition + total).getBytes());
inforaf.close();
mPbs.get(threadId).setProgress(total + lastDownloadTotalSize);
}
is.close();
raf.close();
System.out.println("thread:" + threadId + " download complete...");
} else {
System.out.println("request download failed.");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
synchronized (MainActivity.class) {
runningThreadCount--;
if (runningThreadCount <= 0) {
System.out.println("multi thread download complete.");
for (int i = 0; i < totalThreadCount; i++) {
File finfo = new File(CACHE_DIR + totalThreadCount + getDownloadFileName(path) + i + ".txt");
// System.out.println(finfo.delete());
}
}
}
}
}
}
}