在android開發過程中,經常會接觸到下載資源這個難題,其實這個問題都普遍存在,下面武林技術頻道就來了解android實現多線程斷點續傳功能,大家速速來圍觀吧!

布局:
<?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="繼續" 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("當前進度 :" + progress + " %"); //設置進度條進度 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;//默認下載線程數 //以下為線程狀態 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;//網絡獲取的url private String filePath;//下載到本地的path private int threadCount = DEFAULT_THREAD_COUNT;//下載線程數 private int fileLength;//文件總大小 //使用volatile防止多線程不安全 private volatile int currLength;//當前總共下載的大小 private volatile int runningThreadCount;//正在運行的線程數 private Thread[] mThreads; private String stateDownload = DOWNLOAD_INIT;//當前線程狀態 //下載進度監聽 private DownLoadListener mDownLoadListener; public void setOnDownLoadListener(DownLoadListener mDownLoadListener) { this.mDownLoadListener = mDownLoadListener; } //定義一個下載監聽的接口 public interface DownLoadListener { //返回當前下載進度的百分比 void getProgress(int progress); //下載完成 void onComplete(); //下載失敗 void onFailure(); } /* * 四種有參構造 * */ 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 { //初始化數據 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) {//請求成功,根據文件大小開始分多線程下載 fileLength = conn.getContentLength(); //根據文件大小,先創建一個空文件 //“r“——以只讀方式打開。調用結果對象的任何 write 方法都將導致拋出 IOException。 //“rw“——打開以便讀取和寫入。如果該文件尚不存在,則嘗試創建該文件。 //“rws“—— 打開以便讀取和寫入,對于 “rw”,還要求對文件的內容或元數據的每個更新都同步寫入到底層存儲設備。 //“rwd“——打開以便讀取和寫入,對于 “rw”,還要求對文件內容的每個更新都同步寫入到底層存儲設備。 RandomAccessFile raf = new RandomAccessFile(filePath, "rwd"); raf.setLength(fileLength); raf.close(); //計算各個線程下載的數據段 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++) { //開始位置,獲取上次取消下載的進度,默認返回i*blockLength,即第i個線程開始下載的位置 int startPosition = sp.getInt(SP_NAME + (i + 1), i * blockLength); //結束位置,-1是為了防止上一個線程和下一個線程重復下載銜接處數據 int endPosition = (i + 1) * blockLength - 1; //將最后一個線程結束位置擴大,防止文件下載不完全,大了不影響,小了文件失效 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) { //若線程處于等待狀態,則while循環處于阻塞狀態,無法跳出循環,必須先喚醒線程,才能執行取消任務 if (stateDownload.equals(DOWNLOAD_PAUSE)) onStart(); for (Thread dt : mThreads) { ((DownThread) dt).cancel(); } } } /** * 暫停下載 */ protected void onPause() { if (mThreads != null) stateDownload = DOWNLOAD_PAUSE; } /** * 繼續下載 */ 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;//是否繼續下載 private int threadId; private int startPosition;//開始下載點 private int endPosition;//結束下載點 private int currPosition;//當前線程的下載進度 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這個參數,則返回狀態碼為206,而不是200 if (conn.getResponseCode() == 206) { InputStream is = conn.getInputStream(); RandomAccessFile raf = new RandomAccessFile(filePath, "rwd"); raf.seek(startPosition);//跳到指定位置開始寫數據 int len; byte[] buffer = new byte[1024]; while ((len = is.read(buffer)) != -1) { //是否繼續下載 if (!isGoOn) break; //回調當前進度 if (mDownLoadListener != null) { currLength += len; int progress = (int) ((float) currLength / (float) fileLength * 100); handler.sendEmptyMessage(progress); } raf.write(buffer, 0, len); //寫完后將當前指針后移,為取消下載時保存當前進度做準備 currPosition += len; synchronized (DOWNLOAD_PAUSE) { if (stateDownload.equals(DOWNLOAD_PAUSE)) { DOWNLOAD_PAUSE.wait(); } } } is.close(); raf.close(); //線程計數器-1 runningThreadCount--; //若取消下載,則直接返回 if (!isGoOn) { //此處采用SharedPreferences保存每個線程的當前進度,和三個線程的總下載進度 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); } } } };}以上就是武林技術頻道介紹的android實現多線程斷點續傳功能的全部知識,大家看完后記得分享哦,想了解更多就請關注js.Vevb.com吧!
新聞熱點
疑難解答