android原生实现多线程断点续传功能 本文实例为大家分享了android实现多线程断点续传功能的具体代码,供大家参考,具体内容如下 需求描述: 输入一个下载地址,和要启动的线程数量,点击下载 利用多线程将文件下载到手机端,支持 断点续传. 在前两章
本文实例为大家分享了android实现多线程断点续传功能的具体代码,供大家参考,具体内容如下
需求描述: 输入一个下载地址,和要启动的线程数量,点击下载 利用多线程将文件下载到手机端,支持 断点续传。
在前两章的java 多线程的从基础上进行
效果展示
示例代码:
布局 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/editText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="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/editText2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:ems="10"
android:hint="开启的线程数量"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editText" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="开始下载"
app:layout_constraintStart_toStartOf="parent"
android:onClick="click"
app:layout_constraintTop_toBottomOf="@+id/editText2" />
<LinearLayout
android:id="@+id/ll_proBox"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button">
</LinearLayout>
</android.support.constraint.ConstraintLayout>
item.xml 文件
<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
></ProgressBar>
MainActivity.java
package com.example.www.mutildownload;
import android.Manifest;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
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;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private EditText mEtUrl;
private EditText mEt_thread;
private Button mBtnDownload;
private LinearLayout mLlProBox;
private String path;
private int runningThread;
private int threadCount;
private List<ProgressBar> mPbList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] permissions = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(permissions, 200);
mEtUrl = (EditText) findViewById(R.id.editText);
mEt_thread = (EditText) findViewById(R.id.editText2);
mBtnDownload = (Button) findViewById(R.id.button);
mLlProBox = (LinearLayout) findViewById(R.id.ll_proBox);
//添加 一个进度条的引用
mPbList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
String path = Environment.getExternalStorageDirectory() + "/" + i +".txt";
System.out.println(path);
File file = new File(path);
if(file.exists() && file.length() > 0) {
file.delete();
System.out.println(file.getAbsoluteFile() + "删除成功");
}
}
}
public void click(View v) {
path = mEtUrl.getText().toString().trim();
threadCount = Integer.parseInt(mEt_thread.getText().toString().trim());
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
//先移除进度条 再添加
mPbList.clear();
mLlProBox.removeAllViews();
for (int i = 0; i < threadCount; i++) {
ProgressBar pbView = (ProgressBar)inflater.inflate(R.layout.item, null);
mPbList.add(pbView);
mLlProBox.addView(pbView);
}
new Thread() {
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
int contentLength = conn.getContentLength();
runningThread = threadCount;
System.out.println("length" + contentLength);
RandomAccessFile rafAccessFile = new RandomAccessFile(Environment.getExternalStorageDirectory() + "/" + getFileName(path), "rw");
rafAccessFile.setLength(contentLength);
int blockSize = contentLength / threadCount;
for (int i = 0; i < threadCount; i++) {
int startIndex = i * blockSize; //每个现成下载的开始位置
int endIndex = (i + 1) * blockSize - 1;// 每个线程的结束位置
if (i == threadCount - 1) {
//最后一个线程
endIndex = contentLength - 1;
}
new DownloadThread(startIndex, endIndex, i).start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
public String getFileName(String path) {
int posi = path.lastIndexOf("/") + 1;
return path.substring(posi);
}
private class DownloadThread extends Thread {
private int startIndex;
private int endIndex;
private int threadId;
private int pbMaxSize; // 当前线程下载的最大值
private int pbLastPosition;
public DownloadThread(int startIndex, int endIndex, int threadId) {
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
}
@Override
public void run() {
try {
pbMaxSize = endIndex - startIndex;
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
File file = new File(Environment.getExternalStorageDirectory() + "/" + threadId + ".txt");
if (file.exists() && file.length() > 0) {
FileInputStream fis = new FileInputStream(file);
BufferedReader buff = new BufferedReader(new InputStreamReader(fis));
String lastPosition = buff.readLine();// 读取出来的内容就是上次下载的位置
int lastPos = Integer.parseInt(lastPosition);
System.out.println("线程id:" + threadId + "当前线程下载的位置:-----" + lastPos);
//上次进度条下载的位置
pbLastPosition = lastPos - startIndex;
startIndex = lastPos;
fis.close();
buff.close();
}
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); //固定写法,请求部分资源
int responseCode = conn.getResponseCode(); // 206表示请求部分资源
if (responseCode == 206) {
RandomAccessFile rafAccessFile = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath() + "/" +getFileName(path), "rw");
Log.v("MainActivity", Environment.getExternalStorageDirectory().getPath() + "/" +getFileName(path));
rafAccessFile.seek(startIndex);
InputStream is = conn.getInputStream();
int len = -1;
byte[] buffer = new byte[1024 * 1024];
int total = 0; // 代表当前线程下载的大小
while ((len = is.read(buffer)) != -1) {
rafAccessFile.write(buffer, 0, len);
total += len;
//断点续传, 保存当前线程下载的位置
int currentThreadPosition = startIndex + total; //当前线程下载的位置
// 存储当线程的下载五位置
RandomAccessFile raff = new RandomAccessFile(Environment.getExternalStorageDirectory() + "/" + threadId + ".txt", "rwd");
raff.write(String.valueOf(currentThreadPosition).getBytes());
raff.close();
mPbList.get(threadId).setMax(pbMaxSize);
mPbList.get(threadId).setProgress(pbLastPosition + total);// 设置当前进度条的当前进度
}
rafAccessFile.close();
System.out.println("线程" + threadId + "下载完成");
//删除临时文件
synchronized (MainActivity.DownloadThread.class) {
runningThread--;
if (runningThread == 0) {
for (int i = 0; i < threadCount; i++) {
File deleteFile = new File(Environment.getExternalStorageDirectory() + "/" + i + ".txt");
deleteFile.delete();
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
权限配置
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.www.mutildownload">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<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">
<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>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。
沃梦达教程
本文标题为:android原生实现多线程断点续传功能
基础教程推荐
猜你喜欢
- MVVMLight项目Model View结构及全局视图模型注入器 2023-05-07
- Android Compose自定义TextField实现自定义的输入框 2023-05-13
- iOS中如何判断当前网络环境是2G/3G/4G/5G/WiFi 2023-06-18
- IOS获取系统相册中照片的示例代码 2023-01-03
- Android实现短信验证码输入框 2023-04-29
- Flutter进阶之实现动画效果(三) 2022-10-28
- iOS开发 全机型适配解决方法 2023-01-14
- Android开发Compose集成高德地图实例 2023-06-15
- iOS Crash常规跟踪方法及Bugly集成运用详细介绍 2023-01-18
- iOS开发使用XML解析网络数据 2022-11-12