多线程下载
1.新建:MultiDownloader\app\src\main\res\xml\network_security_config.xml
配置支持HTTP请求
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
2. AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.glsite.multidownloader">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
3.新建MultiDownloader\app\src\main\res\layout\pb.xml
进度条
<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ProgressBar>
4.activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/et_threadcount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="36dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:ems="10"
android:hint="请输入下载线程的数量"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/et_path"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:ems="10"
android:inputType="textPersonName"
android:text="http://192.168.1.130:8080/Day10/QQ.exe"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_threadcount" />
<Button
android:id="@+id/bt_self"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:text="自己的下载方式"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_path" />
<Button
android:id="@+id/bt_other"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:text="第三方的下载方式"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_path" />
<LinearLayout
android:id="@+id/ll_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="100dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.503"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_path"></LinearLayout>
</android.support.constraint.ConstraintLayout>
5.MultiDownloader\lib\src\main\java\com\glsite\lib\MultiDownloader.java
测试:
package com.glsite.lib;
import com.sun.jndi.toolkit.url.UrlUtil;
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;
import java.nio.file.Path;
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 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(getDownloadFileName(path), "rw");
// 创建一个空的文件并且设置它的文件长度等于服务器上的文件长度
raf.setLength(length);
raf.close();
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;
int endPosition = (threadId + 1) * blockSize -1;
if (threadId == (TOTAL_THREAD_COUNT - 1 )) {
endPosition = length - 1;
}
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 + "begin working");
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);
conn.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
int code = conn.getResponseCode();
if (code == 206) {
InputStream is = conn.getInputStream();
RandomAccessFile raf = new RandomAccessFile(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(TOTAL_THREAD_COUNT + getDownloadFileName(path) + threadId + ".txt", "rwd");
inforaf.write(String.valueOf(startPosition + total).getBytes());
inforaf.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());
}
}
}
}
}
}
}
6.MainActivity.java
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());
}
}
}
}
}
}
}