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>

推荐阅读