android實現(xiàn)多線程斷點續(xù)傳功能
本文實例為大家分享了android實現(xiàn)多線程斷點續(xù)傳的具體代碼,供大家參考,具體內(nèi)容如下

布局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <TextView android:text="下載進度" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_marginTop="15dp" android:id="@+id/tv_progress" /> <Button android:text="下載" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/firstBar" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_marginTop="29dp" android:id="@+id/bt" /> <Button android:text="暫停" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@+id/bt" android:layout_toRightOf="@+id/bt" android:layout_toEndOf="@+id/bt" android:layout_marginLeft="15dp" android:layout_marginStart="15dp" android:id="@+id/bt_pause" /> <Button android:text="繼續(xù)" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@+id/bt_pause" android:layout_toRightOf="@+id/bt_pause" android:layout_toEndOf="@+id/bt_pause" android:layout_marginLeft="18dp" android:layout_marginStart="18dp" android:id="@+id/bt_start" /> <ProgressBar android:id="@+id/firstBar" android:layout_width="fill_parent" android:layout_height="wrap_content" style="@android:style/Widget.ProgressBar.Horizontal" android:layout_below="@+id/tv_progress" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_marginTop="21dp" /> </RelativeLayout>
MainActivity :
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.firstBar)
ProgressBar firstBar;
@BindView(R.id.tv_progress)
TextView tvprogress;
@BindView(R.id.bt)
Button bt;
@BindView(R.id.bt_pause)
Button btPause;
@BindView(R.id.bt_start)
Button btStart;
//下載地址
private String loadUrl = "http://mirror.aarnet.edu.au/pub/TED-talks/911Mothers_2010W-480p.mp4";
//文件緩存路徑
private String filePath = Environment.getExternalStorageDirectory() + "/" + "911Mothers.mp4";
DownLoadFile downLoadFile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
downLoadFile = new DownLoadFile(this, loadUrl, filePath, 3);
downLoadFile.setOnDownLoadListener(new DownLoadFile.DownLoadListener() {
@Override
public void getProgress(int progress) {
tvprogress.setText("當(dāng)前進度 :" + progress + " %");
//設(shè)置進度條進度
firstBar.setProgress(progress);
if (progress==100){
Toast.makeText(MainActivity.this, "下載完成", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onComplete() {
Toast.makeText(MainActivity.this, "下載完成", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure() {
Toast.makeText(MainActivity.this, "下載失敗", Toast.LENGTH_SHORT).show();
}
});
}
/*
* 點擊事件
* */
@OnClick({R.id.bt, R.id.bt_pause, R.id.bt_start})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.bt:
downLoadFile.downLoad();
break;
case R.id.bt_pause:
downLoadFile.onPause();
break;
case R.id.bt_start:
downLoadFile.onStart();
break;
}
}
@Override
protected void onDestroy() {
downLoadFile.onDestroy();
super.onDestroy();
}
}
DownLoadFile:
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Message;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class DownLoadFile {
private static final String SP_NAME = "download_file";
private static final String CURR_LENGTH = "curr_length";
private static final int DEFAULT_THREAD_COUNT = 4;//默認(rèn)下載線程數(shù)
//以下為線程狀態(tài)
private static final String DOWNLOAD_INIT = "1"; //初始化
private static final String DOWNLOAD_ING = "2"; //正在下載
private static final String DOWNLOAD_PAUSE = "3"; //暫停
private Context mContext;
private String loadUrl;//網(wǎng)絡(luò)獲取的url
private String filePath;//下載到本地的path
private int threadCount = DEFAULT_THREAD_COUNT;//下載線程數(shù)
private int fileLength;//文件總大小
//使用volatile防止多線程不安全
private volatile int currLength;//當(dāng)前總共下載的大小
private volatile int runningThreadCount;//正在運行的線程數(shù)
private Thread[] mThreads;
private String stateDownload = DOWNLOAD_INIT;//當(dāng)前線程狀態(tài)
//下載進度監(jiān)聽
private DownLoadListener mDownLoadListener;
public void setOnDownLoadListener(DownLoadListener mDownLoadListener) {
this.mDownLoadListener = mDownLoadListener;
}
//定義一個下載監(jiān)聽的接口
public interface DownLoadListener {
//返回當(dāng)前下載進度的百分比
void getProgress(int progress);
//下載完成
void onComplete();
//下載失敗
void onFailure();
}
/*
* 四種有參構(gòu)造
* */
public DownLoadFile(Context mContext, String loadUrl, String filePath) {
this(mContext, loadUrl, filePath, DEFAULT_THREAD_COUNT, null);
}
public DownLoadFile(Context mContext, String loadUrl, String filePath, DownLoadListener mDownLoadListener) {
this(mContext, loadUrl, filePath, DEFAULT_THREAD_COUNT, mDownLoadListener);
}
public DownLoadFile(Context mContext, String loadUrl, String filePath, int threadCount) {
this(mContext, loadUrl, filePath, threadCount, null);
}
public DownLoadFile(Context mContext, String loadUrl, String filePath, int threadCount, DownLoadListener mDownLoadListener) {
this.mContext = mContext;
this.loadUrl = loadUrl;
this.filePath = filePath;
this.threadCount = threadCount;
runningThreadCount = 0;
this.mDownLoadListener = mDownLoadListener;
}
/**
* 開始下載
*/
protected void downLoad() {
//在線程中運行,防止anr
new Thread(new Runnable() {
@Override
public void run() {
try {
//初始化數(shù)據(jù)
if (mThreads == null)
mThreads = new Thread[threadCount];
//建立連接請求
URL url = new URL(loadUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
int code = conn.getResponseCode();//獲取返回碼
if (code == 200) {//請求成功,根據(jù)文件大小開始分多線程下載
fileLength = conn.getContentLength();
//根據(jù)文件大小,先創(chuàng)建一個空文件
//“r“——以只讀方式打開。調(diào)用結(jié)果對象的任何 write 方法都將導(dǎo)致拋出 IOException。
//“rw“——打開以便讀取和寫入。如果該文件尚不存在,則嘗試創(chuàng)建該文件。
//“rws“—— 打開以便讀取和寫入,對于 “rw”,還要求對文件的內(nèi)容或元數(shù)據(jù)的每個更新都同步寫入到底層存儲設(shè)備。
//“rwd“——打開以便讀取和寫入,對于 “rw”,還要求對文件內(nèi)容的每個更新都同步寫入到底層存儲設(shè)備。
RandomAccessFile raf = new RandomAccessFile(filePath, "rwd");
raf.setLength(fileLength);
raf.close();
//計算各個線程下載的數(shù)據(jù)段
int blockLength = fileLength / threadCount;
SharedPreferences sp = mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
//獲取上次取消下載的進度,若沒有則返回0
currLength = sp.getInt(CURR_LENGTH, 0);
for (int i = 0; i < threadCount; i++) {
//開始位置,獲取上次取消下載的進度,默認(rèn)返回i*blockLength,即第i個線程開始下載的位置
int startPosition = sp.getInt(SP_NAME + (i + 1), i * blockLength);
//結(jié)束位置,-1是為了防止上一個線程和下一個線程重復(fù)下載銜接處數(shù)據(jù)
int endPosition = (i + 1) * blockLength - 1;
//將最后一個線程結(jié)束位置擴大,防止文件下載不完全,大了不影響,小了文件失效
if ((i + 1) == threadCount)
endPosition = endPosition * 2;
mThreads[i] = new DownThread(i + 1, startPosition, endPosition);
mThreads[i].start();
}
} else {
handler.sendEmptyMessage(FAILURE);
}
} catch (Exception e) {
e.printStackTrace();
handler.sendEmptyMessage(FAILURE);
}
}
}).start();
}
/**
* 取消下載
*/
protected void cancel() {
if (mThreads != null) {
//若線程處于等待狀態(tài),則while循環(huán)處于阻塞狀態(tài),無法跳出循環(huán),必須先喚醒線程,才能執(zhí)行取消任務(wù)
if (stateDownload.equals(DOWNLOAD_PAUSE))
onStart();
for (Thread dt : mThreads) {
((DownThread) dt).cancel();
}
}
}
/**
* 暫停下載
*/
protected void onPause() {
if (mThreads != null)
stateDownload = DOWNLOAD_PAUSE;
}
/**
* 繼續(xù)下載
*/
protected void onStart() {
if (mThreads != null)
synchronized (DOWNLOAD_PAUSE) {
stateDownload = DOWNLOAD_ING;
DOWNLOAD_PAUSE.notifyAll();
}
}
protected void onDestroy() {
if (mThreads != null)
mThreads = null;
}
private class DownThread extends Thread {
private boolean isGoOn = true;//是否繼續(xù)下載
private int threadId;
private int startPosition;//開始下載點
private int endPosition;//結(jié)束下載點
private int currPosition;//當(dāng)前線程的下載進度
private DownThread(int threadId, int startPosition, int endPosition) {
this.threadId = threadId;
this.startPosition = startPosition;
currPosition = startPosition;
this.endPosition = endPosition;
runningThreadCount++;
}
@Override
public void run() {
SharedPreferences sp = mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
try {
URL url = new URL(loadUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
conn.setConnectTimeout(5000);
//若請求頭加上Range這個參數(shù),則返回狀態(tài)碼為206,而不是200
if (conn.getResponseCode() == 206) {
InputStream is = conn.getInputStream();
RandomAccessFile raf = new RandomAccessFile(filePath, "rwd");
raf.seek(startPosition);//跳到指定位置開始寫數(shù)據(jù)
int len;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
//是否繼續(xù)下載
if (!isGoOn)
break;
//回調(diào)當(dāng)前進度
if (mDownLoadListener != null) {
currLength += len;
int progress = (int) ((float) currLength / (float) fileLength * 100);
handler.sendEmptyMessage(progress);
}
raf.write(buffer, 0, len);
//寫完后將當(dāng)前指針后移,為取消下載時保存當(dāng)前進度做準(zhǔn)備
currPosition += len;
synchronized (DOWNLOAD_PAUSE) {
if (stateDownload.equals(DOWNLOAD_PAUSE)) {
DOWNLOAD_PAUSE.wait();
}
}
}
is.close();
raf.close();
//線程計數(shù)器-1
runningThreadCount--;
//若取消下載,則直接返回
if (!isGoOn) {
//此處采用SharedPreferences保存每個線程的當(dāng)前進度,和三個線程的總下載進度
if (currPosition < endPosition) {
sp.edit().putInt(SP_NAME + threadId, currPosition).apply();
sp.edit().putInt(CURR_LENGTH, currLength).apply();
}
return;
}
if (runningThreadCount == 0) {
sp.edit().clear().apply();
handler.sendEmptyMessage(SUCCESS);
handler.sendEmptyMessage(100);
mThreads = null;
}
} else {
sp.edit().clear().apply();
handler.sendEmptyMessage(FAILURE);
}
} catch (Exception e) {
sp.edit().clear().apply();
e.printStackTrace();
handler.sendEmptyMessage(FAILURE);
}
}
public void cancel() {
isGoOn = false;
}
}
//請求成功
private final int SUCCESS = 0x00000101;
//請求失敗
private final int FAILURE = 0x00000102;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (mDownLoadListener != null) {
if (msg.what == SUCCESS) {
mDownLoadListener.onComplete();
} else if (msg.what == FAILURE) {
mDownLoadListener.onFailure();
} else {
mDownLoadListener.getProgress(msg.what);
}
}
}
};
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android多線程斷點續(xù)傳下載實現(xiàn)代碼
- Android 使用AsyncTask實現(xiàn)多線程斷點續(xù)傳
- Android 使用AsyncTask實現(xiàn)多任務(wù)多線程斷點續(xù)傳下載
- Android多線程斷點續(xù)傳下載示例詳解
- Android FTP 多線程斷點續(xù)傳下載\上傳的實例
- Android多線程+單線程+斷點續(xù)傳+進度條顯示下載功能
- Android實現(xiàn)網(wǎng)絡(luò)多線程斷點續(xù)傳下載功能
- android實現(xiàn)多線程下載文件(支持暫停、取消、斷點續(xù)傳)
- Android實現(xiàn)網(wǎng)絡(luò)多線程斷點續(xù)傳下載實例
- Android實現(xiàn)多線程斷點續(xù)傳
相關(guān)文章
Android編程實現(xiàn)系統(tǒng)重啟與關(guān)機的方法
這篇文章主要介紹了Android編程實現(xiàn)系統(tǒng)重啟與關(guān)機的方法,較為詳細(xì)的分析了Android運行原理與源碼剖析,講述了Android編程實現(xiàn)系統(tǒng)重啟與關(guān)機的相關(guān)技巧與注意事項,需要的朋友可以參考下2016-02-02
在Android中動態(tài)添加Panel框架的實現(xiàn)代碼
項目經(jīng)常會有這種需求,就是想動態(tài)的在某個界面上添加一個Panel。比如,有一個按鈕,點擊后會顯示下拉菜單式的界面。這種需求,就屬于動態(tài)添加一個Panel。需求多了,就要研究是否可以抽象出通用的框架代碼,以方便開發(fā),所以就有了以下內(nèi)容2013-05-05
淺析android studio3.5中使用recycleview的包
這篇文章主要介紹了android studio3.5中使用recycleview的包,本文雖然內(nèi)容不長,但是給出了思路,需要的朋友可以參考下2019-11-11
Kotlin學(xué)習(xí)筆記之const val與val
這篇文章主要給大家介紹了關(guān)于Kotlin學(xué)習(xí)筆記之const val與val的相關(guān)資料,并給大家介紹了const val和val區(qū)別以及Kotlin中var和val的區(qū)別,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05
Android優(yōu)化提升應(yīng)用啟動速度及Splash頁面的設(shè)計
這篇文章主要介紹了Android性能優(yōu)化的一些相關(guān)資料,文章圍繞提升應(yīng)用啟動速度及Splash頁面的設(shè)計的內(nèi)容展開介紹,需要的朋友可以參考一下,希望對你有所幫助2021-12-12
Android studio導(dǎo)出APP測試包和構(gòu)建正式簽名包
大家好,本篇文章主要講的是Android studio導(dǎo)出APP測試包和構(gòu)建正式簽名包,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下2021-12-12

