您好,登錄后才能下訂單哦!
各位父老鄉親,我單漢三又回來了,今天為大家帶來一個用原生的安卓寫的多線程斷點下載Demo。
通過本文你可以學習到:
Demo是在TV上運行的,圖片顯示的問題不要糾結了。
文件下載的Demo已完成,沒時間上傳與講解,今天為您展示并講解一下,純原生的東西來下載文件,希望可以幫你理解更多安卓比較基礎的問題。
我們的思路:建立一個數據庫,兩個表,一個用來保存網絡數據,一個保存本地下載的進度等等。在點擊下載按鈕的時候啟動DownloadService,進行比對之后下載
先看一下Demo的目錄結構:
所有的步驟在代碼里有非常詳細的講解,一定要看代碼(下面是抽取的幾個重要的類講解)!
數據庫的建立與DAO
/** * Created by Administrator on 2017/3/6 0006. */ public class DownLoadDBHelper extends SQLiteOpenHelper { /** * DownLoadDBHelper用于創建數據庫,如果不會使用原生的建庫的話 * * 跟隨小司機我的腳步來一起練一練 * 建兩個表: * download_info表存儲下載信息 * localdownload_info表存儲本地下載信息 * 之后對比兩個表進行繼續下載等等 */ public static String DATABASE_NAME = "downloadFILES.db"; public static String TABLE_DOWNLOAD_INFO = "download_info"; public static String TABLE_LOCALDOWNLOAD_INFO = "localdownload_info"; private static int version = 1; public DownLoadDBHelper(Context context) { super(context, DATABASE_NAME, null, version); } @Override public void onCreate(SQLiteDatabase db) { /*在此進行創建數據庫和表格,來一起動手寫一遍,就是兩個sqlite語句*/ db.execSQL("create table " + TABLE_DOWNLOAD_INFO + "(" + "id integer PRIMARY KEY AUTOINCREMENT," + "thread_id integer," + "start_position integer," + "end_position integer," + " completed_size integer," + "url varchar(100))"); db.execSQL("create table " + TABLE_LOCALDOWNLOAD_INFO + "(" + "id integer PRIMARY KEY AUTOINCREMENT," + "name varchar(50)," + "url varchar(100)," + "completedSize integer," + "fileSize integer," + "status integer)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /*數據庫更新升級,檢測到版本的變化,發現版本號不一樣,就會自動調用onUpgrade函數 * 新版本號和老版本號都會作為onUpgrade函數的參數傳進來,便于開發者知道數據庫應該從哪個版本升級到哪個版本。 * */ String sql = "drop table if exists " + TABLE_DOWNLOAD_INFO + ""; String sqlOne = "drop table if exists " + TABLE_LOCALDOWNLOAD_INFO + ""; db.execSQL(sql); db.execSQL(sqlOne); onCreate(db);//刪除數據庫,重新創建。這里只是簡單的,并沒有添加或者減少數據庫中的其他字段 } }
DAO對數據庫進行增刪改查
/** * Created by ShanCanCan on 2017/3/6 0006. */ public class Dao { /* * DAO層主要是做數據持久層的工作,負責與數據庫進行聯絡的一些任務都封裝在此。 * DAO層所定義的接口里的方法都大同小異,這是由我們在DAO層對數據庫訪問的操作來決定的, * 對數據庫的操作,我們基本要用到的就是新增,更新,刪除,查詢等方法。 * 因而DAO層里面基本上都應該要涵蓋這些方法對應的操作。 * */ private static Dao dao; private static DownLoadDBHelper dbHelper; public static final byte[] Lock = new byte[0]; //新建兩個字節作為對象鎖 public static final byte[] file_Lock = new byte[0]; public Dao() {//空構造方法, } public static synchronized Dao getInstance(Context context) {//本demo用單例模式中的懶漢模式+線程不安全 線程安全的代價是效率變低 if (dao == null) { dao = new Dao(); dbHelper = new DownLoadDBHelper(context); } return dao; } /* public static synchronized Dao getInstance(Context context) {//本demo用單例模式中的懶漢模式+線程安全 線程安全的代價是效率變低,99%情況下不需要同步 if (dao == null) { //你可以在這兩個方法中隨便選擇一個 dao = new Dao(); dbHelper = new DownLoadDBHelper(context); } return dao; }*/ /*************************************** 下方Dao層中對數據庫的增、刪、改、查 *********************************************************/ /** * 檢查本地下載記錄,是否下載過 * * @param url * @return */ public boolean isExist(String url) { SQLiteDatabase database = dbHelper.getReadableDatabase(); //獲取本app所創建的數據庫 String sql = "select count(*) from " + TABLE_LOCALDOWNLOAD_INFO + " where url=?"; //查詢語句,查詢總共有多少條的語句 Cursor cursor = database.rawQuery(sql, new String[]{url}); /** * * @Cursor * Cursor 是每行的集合。 * 使用 moveToFirst() 定位第一行。 * 你必須知道每一列的名稱。 * 你必須知道每一列的數據類型。 * Cursor 是一個隨機的數據源。 * 所有的數據都是通過下標取得。 * Cursor按照我的理解就是一個箭頭,指到哪一行就是那一行的集合 * 比較重要的方法有:close(),moveToFirst(),moveToNext(),moveToLast(),moveToPrevious(),getColumnCount()等。 * * @rawQuery * rawQuery是直接使用SQL語句進行查詢的,也就是第一個參數字符串, * 在字符串內的“?”會被后面的String[]數組逐一對換掉 * cursor用完之后要關閉,cursor用完之后要關閉,cursor用完之后要關閉。重要的事情說三遍!!! * * */ cursor.moveToFirst(); int count = cursor.getInt(0); cursor.close(); return count > 0; } /** * 是否為首次下載 * * @param url * @return */ public boolean isFirstDownload(String url) { SQLiteDatabase database = dbHelper.getReadableDatabase(); String sql = "select count(*) from " + TABLE_DOWNLOAD_INFO + " where url=?"; Cursor cursor = database.rawQuery(sql, new String[]{url}); cursor.moveToFirst(); int count = cursor.getInt(0); cursor.close(); return count == 0; } /** * 保存下載的具體信息 保存所下載的list集合中的數據 * * @param infos * @param context */ public void saveInfos(List<DownLoadInfo> infos, Context context) { /** * 事務(Transaction)是并發控制的單位,是用戶定義的一個操作序列。 * 這些操作要么都做,要么都不做,是一個不可分割的工作單位。 * 通過事務,SQL Server能將邏輯相關的一組操作綁定在一起, * 以便保持數據的完整性。 * * 事務具有四個特征:原子性( Atomicity )、一致性( Consistency )、 * 隔離性( Isolation )和持續性( Durability )。這四個特性簡稱為 ACID 特性。 * * */ synchronized (Lock) { SQLiteDatabase database = dbHelper.getWritableDatabase(); database.beginTransaction();//開啟事務 try {//如果有異常,在這里捕獲 for (DownLoadInfo info : infos) {//for循環將數據存入數據庫 String sql = "insert into " + TABLE_DOWNLOAD_INFO + "(thread_id,start_position, end_position, completed_size, url) values (?,?,?,?,?)"; Object[] bindArgs = {info.getThreadId(), info.getStartPosition(), info.getEndPosition(), info.getCompletedSize(), info.getUrl()}; database.execSQL(sql, bindArgs); } database.setTransactionSuccessful();//結束事務 } catch (SQLException e) { e.printStackTrace(); } finally { database.endTransaction();//關閉事務 } } } /** * 得到下載具體信息 * * @param urlstr * @return List<DownloadInfo> 一個下載器信息集合器,里面存放了每條線程的下載信息 */ public List<DownLoadInfo> getInfos(String urlstr) { List<DownLoadInfo> list = new ArrayList<DownLoadInfo>(); SQLiteDatabase database = dbHelper.getReadableDatabase(); String sql = "select thread_id, start_position, end_position, completed_size, url from " + TABLE_DOWNLOAD_INFO + " where url=?"; Cursor cursor = database.rawQuery(sql, new String[]{urlstr}); while (cursor.moveToNext()) {//通過cursor取到下載器信息,循環遍歷,得到下載器集合 DownLoadInfo info = new DownLoadInfo(cursor.getInt(0), cursor.getInt(1), cursor.getInt(2), cursor.getInt(3), cursor.getString(4)); list.add(info); } cursor.close(); return list; } /** * 本地下載列表添加記錄,添加本地數據庫信息,完成度等等 * * @param fileStatus **/ public void insertFileStatus(FileStatus fileStatus) { synchronized (file_Lock) {//異步加開啟事務,保證數據的完整性 SQLiteDatabase database = dbHelper.getWritableDatabase(); database.beginTransaction(); try { String sql = "insert into " + TABLE_LOCALDOWNLOAD_INFO + " (name,url,completedSize,fileSize,status) values(?,?,?,?,?)"; Object[] bindArgs = {fileStatus.getName(), fileStatus.getUrl(), fileStatus.getCompletedSize(), fileStatus.getFileSize(), fileStatus.getStatus()}; database.execSQL(sql, bindArgs); database.setTransactionSuccessful(); } catch (SQLException e) { e.printStackTrace(); } finally { database.endTransaction(); } } } /** * @param context * @param compeletedSize * @param threadId * @param urlstr 這里是更新數據庫,建議在保存一個表格的時候就對另一個表格數據庫進行更新 */ public void updataInfos(int threadId, int compeletedSize, String urlstr, Context context) { synchronized (Lock) { String sql = "update " + TABLE_DOWNLOAD_INFO + "set completed_size = ? where thread_id =? and url=?"; String localSql = "update " + TABLE_LOCALDOWNLOAD_INFO + "set completedSize = (select sum(completed_size) from " + TABLE_DOWNLOAD_INFO + "where url=? group by url ) where url=?"; Object[] bindArgs = {compeletedSize, threadId, urlstr}; Object[] localArgs = {urlstr, urlstr}; SQLiteDatabase database = dbHelper.getWritableDatabase(); database.beginTransaction(); try { database.execSQL(sql, bindArgs); database.execSQL(localSql, localArgs); database.setTransactionSuccessful(); } catch (SQLException e) { e.printStackTrace(); } finally { database.endTransaction(); } } } /** * @param url 更新文件的狀態,0為正在下載,1為已經下載完成,2為下載出錯 **/ public void updateFileStatus(String url) { synchronized (file_Lock) { String sql = "update " + TABLE_LOCALDOWNLOAD_INFO + " set status = ? where url = ?"; Object[] bindArgs = {1, url}; SQLiteDatabase database = dbHelper.getWritableDatabase(); database.beginTransaction(); try { database.execSQL(sql, bindArgs); database.setTransactionSuccessful(); } catch (SQLException e) { e.printStackTrace(); } finally { database.endTransaction(); } } } /** * @return List<FileStatus> * 取出本地下載列表數據,如在重新進入應用時,要重新把進度之類的設置好 **/ public List<FileStatus> getFileStatus() { List<FileStatus> list = new ArrayList<FileStatus>(); SQLiteDatabase database = dbHelper.getReadableDatabase(); //String sql = "slect * from " + TABLE_LOCALDOWNLOAD_INFO + ""; //不能用,需要哪些條件就在語句中寫出哪些條件 String sql = "select name, url, status, completedSize, fileSize from " + TABLE_LOCALDOWNLOAD_INFO + ""; Cursor cursor = database.rawQuery(sql, null); while (cursor.moveToNext()) { FileStatus fileState = new FileStatus(cursor.getString(0), cursor.getString(1), cursor.getInt(2), cursor.getInt(3), cursor.getInt(4)); list.add(fileState); } cursor.close(); return list; } /** * @param url * @param completeSize * @param status 更新文件的下載狀態 **/ public void updateFileDownStatus(int completeSize, int status, String url) { synchronized (file_Lock) { String sql = "update " + TABLE_LOCALDOWNLOAD_INFO + " set completedSize = ?,status = ? where url = ?"; SQLiteDatabase database = dbHelper.getWritableDatabase(); database.beginTransaction(); try { Object[] bindArgs = {completeSize, status, url}; database.execSQL(sql, bindArgs); database.delete(TABLE_DOWNLOAD_INFO, "url = ?", new String[]{url}); database.setTransactionSuccessful(); } catch (SQLException e) { e.printStackTrace(); } finally { database.endTransaction(); } } } /** * @param url 獲取文件名稱 **/ public String getFileName(String url) { String result = ""; String sql = "select name from " + TABLE_LOCALDOWNLOAD_INFO + " where url = ?"; SQLiteDatabase database = dbHelper.getReadableDatabase(); Cursor cursor = database.rawQuery(sql, new String[]{url}); if (cursor.moveToNext()) { result = cursor.getString(0); } cursor.close(); return result; } /** * 刪除文件之后,要刪除下載的數據,一個是用戶可以重新下載 * 另一個是表再次添加一條數據的時候不出現錯誤 * * @param url */ public void deleteFile(String url) { SQLiteDatabase database = dbHelper.getWritableDatabase(); database.beginTransaction(); try { database.delete(TABLE_DOWNLOAD_INFO, " url = ?", new String[]{url}); database.delete(TABLE_LOCALDOWNLOAD_INFO, " url = ?", new String[]{url}); database.setTransactionSuccessful(); } catch (Exception e) { e.printStackTrace(); } finally { database.endTransaction(); } } /** * 關閉數據庫 * * @close */ public void closeDB() { dbHelper.close(); } }
DownloadService 主要代碼
@SuppressLint("HandlerLeak") public class DownloadService extends Service { public IBinder binder = new MyBinder(); public class MyBinder extends Binder { public DownloadService getService() { return DownloadService.this; } } @Override public IBinder onBind(Intent intent) { return binder; } public static int number = 0; // 文件保存地址 public final String savePath = "/mnt/sdcard/MultiFileDownload/"; // 存放下載列表的引用 public static List<FileStatus> list = new ArrayList<FileStatus>(); public static Map<String, String> localDownList = new HashMap<String, String>(); // 保存每個文件下載的下載器 public static Map<String, Downloader> downloaders = new HashMap<String, Downloader>(); // 每個下載文件完成的長度 private Map<String, Integer> completeSizes = new HashMap<String, Integer>(); // 每個下載文件的總長度 private Map<String, Integer> fileSizes = new HashMap<String, Integer>(); private Downloader downloader; private int threadCount = 5; private Dao dao; private DownLoadCallback loadCallback; private FileStatus mFileStatus = null; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 1) { String url = (String) msg.obj; int length = msg.arg1; int completeSize = completeSizes.get(url); int fileSize = fileSizes.get(url); completeSize += length; completeSizes.put(url, completeSize); synchronized (list) { for (int i = 0; i < list.size(); i++) { FileStatus fileStatus = list.get(i); if (fileStatus.getUrl().equals(url)) { if (completeSize == fileStatus.getFileSize()) { System.out.println("-----------下載完成:"+fileStatus.getName()+":"+completeSize+"-----"+fileStatus.getFileSize()); list.set(i, new FileStatus(fileStatus.getName(), fileStatus.getUrl(), 1, completeSize, fileStatus.getFileSize())); dao.updateFileDownStatus(completeSize, 1, url); } else { list.set(i, new FileStatus(fileStatus.getName(), fileStatus.getUrl(), 0, completeSize, fileStatus.getFileSize())); } mFileStatus = list.get(i); } } this.postDelayed(new Runnable() { @Override public void run() { if (loadCallback != null && mFileStatus != null) { loadCallback.refreshUI(mFileStatus); } } }, 1000); } } } }; @Override public void onCreate() { super.onCreate(); dao = Dao.getInstance(this); list = dao.getFileStatus(); for (FileStatus fileStatus : list) { localDownList.put(fileStatus.getUrl(), fileStatus.getUrl()); } Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { number++; } }, 0, 1000); } public void download(final Button button, final String url, final String name, final Handler mHandler) { if (dao.isExist(url)) { Toast.makeText(this, "別點了,已經在下載了", Toast.LENGTH_SHORT).show(); return; } final String fileName = name + url.substring(url.lastIndexOf(".")); new Thread(new Runnable() { @Override public void run() { downloader = downloaders.get(url); if (downloader == null) { downloader = new Downloader(url, savePath, fileName, threadCount, DownloadService.this, handler); downloaders.put(url, downloader); } if (downloader.isDownloading()) return; LoadInfo loadInfo = downloader.getDownloaderInfors(); if(loadInfo != null) { FileStatus fileStatus = new FileStatus(fileName, url, 0, loadInfo.getComplete(), loadInfo.getFileSize()); dao.insertFileStatus(fileStatus); completeSizes.put(url, loadInfo.getComplete()); fileSizes.put(url, fileStatus.getFileSize()); list.add(fileStatus); localDownList.put(url, url); downloader.download(); Message msg = new Message(); msg.what = 1; msg.obj = button; mHandler.sendMessage(msg); } else { Message msg = new Message(); msg.what = 2; msg.obj = button; mHandler.sendMessage(msg); } } }).start(); } //暫停下載 public void Pause(Downloader downloader) { downloader.pause(); } //繼續下載 public void reDownload(final Button button, final String url, final String name, final Handler mHandler) { new Thread(new Runnable() { @Override public void run() { String fileName = dao.getFileName(url); downloader = downloaders.get(url); if (downloader == null) { downloader = new Downloader(url, savePath, fileName, threadCount, DownloadService.this, handler); downloaders.put(url, downloader); } if (downloader.isDownloading()) return; LoadInfo loadInfo = downloader.getDownloaderInfors(); if(loadInfo != null && !fileName.equals("")) { if(!completeSizes.containsKey(url)) { completeSizes.put(url, loadInfo.getComplete()); } if(!fileSizes.containsKey(url)) { fileSizes.put(url, loadInfo.getFileSize()); } downloader.download(); Message msg = new Message(); msg.what = 1; msg.obj = button; mHandler.sendMessage(msg); } else { Message msg = new Message(); msg.what = 2; msg.obj = button; mHandler.sendMessage(msg); } } }).start(); } public void delete(final String url) { Downloader down = downloaders.get(url); if(down != null) { down.pause(); } handler.postDelayed(new Runnable() { @Override public void run() { dao.deleteFile(url); for (int i = 0; i < list.size(); i++) { FileStatus fileStatus = list.get(i); if (fileStatus.getUrl().equals(url)) { list.remove(i); } } localDownList.remove(url); downloaders.remove(url); completeSizes.remove(url); fileSizes.remove(url); if(loadCallback != null) { loadCallback.deleteFile(url); } } }, 1000); } public interface DownLoadCallback { public void refreshUI(FileStatus fileStatus); public void deleteFile(String url); } public void setLoadCallback(DownLoadCallback loadCallback) { this.loadCallback = loadCallback; } }
下載工具類DownLoadUtil
/** * Created by ShanCanCan on 2017/3/7 0007. */ public class DownLoadUtil { /** * 此類的主要功能 * 1、檢查是否下載 * 2、下載文件,文件的下載采用httpurlconnection */ private String downPath;// 下載路徑 private String savePath;// 保存路徑 private String fileName;// 文件名稱 private int threadCount;// 線程數 private Handler mHandler; private Dao dao; private Context context; private int fileSize;// 文件大小 private int range; private List<DownLoadInfo> infos;// 存放下載信息類的集合 private int state = INIT; private static final int INIT = 1;// 定義三種下載的狀態:初始化狀態,正在下載狀態,暫停狀態 private static final int DOWNLOADING = 2; private static final int PAUSE = 3; /** * 構造方法,獲取dao的對象 * * @param downPath * @param savePath * @param fileName * @param threadCount * @param context * @param mHandler */ public DownLoadUtil(String downPath, String savePath, String fileName, int threadCount, Handler mHandler, Context context) { this.downPath = downPath; this.savePath = savePath; this.fileName = fileName; this.threadCount = threadCount; this.mHandler = mHandler; this.context = context; dao = Dao.getInstance(context); } /** * 判斷是否PAUSE **/ public boolean isPause() { return state == PAUSE; } /** * 判斷是否DOWNLOADING */ public boolean isDownloading() { return state == DOWNLOADING; } /** * @param url 判斷是否是第一次下載,利用dao查詢數據庫中是否有下載這個地址的記錄 */ private boolean isFirst(String url) { return dao.isFirstDownload(url); } /** * 獲取要下載的東西 */ public LoadItemInfo getDownloadInfos() { if (isFirst(downPath)) { if (initFirst()) {//如果是第一次下載的話,要進行初始化,1.獲得下載文件的長度 2.創建文件,設置文件的大小 range = this.fileSize / this.threadCount; infos = new ArrayList<DownLoadInfo>(); //這里就是啟動多線程下載,看出來了嗎?配合RandomAccessFile。每一個DownLoadInfo就是RandomAccessFile文件的一部分 for (int i = 0; i < this.threadCount - 1; i++) { DownLoadInfo info = new DownLoadInfo(i, i * range, (i + 1) * range - 1, 0, downPath); infos.add(info); } DownLoadInfo info = new DownLoadInfo(this.threadCount - 1, (this.threadCount - 1) * range, this.fileSize, 0, downPath); infos.add(info); dao.saveInfos(infos, this.context); //(String urlDownload, int completePercent, int fileSize) LoadItemInfo loadInfo = new LoadItemInfo(this.downPath, 0, this.fileSize); return loadInfo; } else { return null; } } else { //不是第一次下載,我們應該怎么做呢?從數據庫里面取回來 infos = dao.getInfos(this.downPath); if (infos != null && infos.size() > 0) { int size = 0; int completeSize = 0; for (DownLoadInfo info : infos) { completeSize += info.getCompletedSize(); size += info.getEndPosition() - info.getStartPosition() + this.threadCount - 1; } LoadItemInfo loadInfo = new LoadItemInfo(this.downPath, completeSize, size); return loadInfo; } else { return null; } } } // 設置暫停 public void pause() { state = PAUSE; } // 重置下載狀態,將下載狀態設置為init初始化狀態 public void reset() { state = INIT; } /** * 基本上,RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream結合起來,再加上它自己的一些方法, * 比如定位用的getFilePointer( ),在文件里移動用的seek( ),以及判斷文件大小的length( )、skipBytes()跳過多少字節數。 * 此外,它的構造函數還要一個表示以只讀方式("r"),還是以讀寫方式("rw")打開文件的參數 (和C的fopen( )一模一樣)。它不支持只寫文件。 */ private boolean initFirst() { boolean result = true; HttpURLConnection conn = null; RandomAccessFile randomFile = null; URL url = null; try { url = new URL(downPath); conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5 * 1000); conn.setRequestMethod("GET"); // 如果http返回的代碼是200或者206則為連接成功 if (conn.getResponseCode() == 200 || conn.getResponseCode() == 206) //狀態碼(206),表示服務器已經執行完部分對資源的GET請求 { fileSize = conn.getContentLength();// 得到文件的大小 if (fileSize <= 0) { //("網絡故障,無法獲取文件大小"); return false; } File dir = new File(savePath); // 如果文件目錄不存在,則創建 if (!dir.exists()) { if (dir.mkdirs()) { //("mkdirs success."); } } File file = new File(this.savePath, this.fileName); randomFile = new RandomAccessFile(file, "rwd"); randomFile.setLength(fileSize);// 設置保存文件的大小 randomFile.close(); conn.disconnect(); } } catch (Exception e) { e.printStackTrace(); result = false; } finally { if (randomFile != null) { try { randomFile.close(); } catch (IOException e) { e.printStackTrace(); } } if (conn != null) { conn.disconnect(); } } return result; } /** * 下面的這個方法就是開啟多線程進行下載了數據了 */ public void downLoad() { if (infos != null) { if (state == DOWNLOADING) { return; } state = DOWNLOADING;// 把狀態設置為正在下載 for (DownLoadInfo info : infos) {//為什么說我們是多線程呢?因為我們分別用新線程去下載剛才分割好的一個RandomAccessFile文件 new DownLoadThread(info.getThreadId(), info.getStartPosition(), info.getEndPosition(), info.getCompletedSize(), info.getUrl(), this.context).start(); } } } /** * 現在要創建線程用來下載了,這里采用內部類 */ public class DownLoadThread extends Thread { private int threadId; private int startPostion; private int endPostion; private int compeletedSize; private String url; private Context context; public static final int PROGRESS = 1; public DownLoadThread(int threadId, int startPostion, int endPostion, int compeletedSize, String url, Context context) {//構造方法,傳入特定的參數 this.threadId = threadId; this.startPostion = startPostion; this.endPostion = endPostion; this.compeletedSize = compeletedSize; this.url = url; this.context = context; } //開始下載 @Override public void run() { HttpURLConnection conn = null; RandomAccessFile randomAccessFile = null; InputStream inStream = null; File file = new File(savePath, fileName); URL url = null; try { url = new URL(this.url); conn = (HttpURLConnection) url.openConnection(); constructConnection(conn); if (conn.getResponseCode() == 200 || conn.getResponseCode() == 206) { randomAccessFile = new RandomAccessFile(file, "rwd"); randomAccessFile.seek(this.startPostion + this.compeletedSize);//RandomAccessFile移動指針,到需要下載的塊 inStream = conn.getInputStream(); byte buffer[] = new byte[4096];//這個4096為么子呢?我也不知道,就是看阿里的人下載apk的時候都用4096,我也用 int length = 0; while ((length = inStream.read(buffer, 0, buffer.length)) != -1) { randomAccessFile.write(buffer, 0, length); compeletedSize += length; // 更新數據庫中的下載信息 dao.updataInfos(threadId, compeletedSize, this.url, this.context); // 用消息將下載信息傳給進度條,對進度條進行更新 Message message = Message.obtain(); message.what = PROGRESS; message.obj = this.url; message.arg1 = length; mHandler.sendMessage(message);// 給DownloadService發送消息 if (state == PAUSE) { //("-----pause-----"); return; } } // ("------------線程:" + this.threadId + "下載完成"); } } catch (IOException e) { e.printStackTrace(); //("-----下載異常-----"); 這里下載異常我就不處理了,你可以發一條重新下載的消息 } finally {//用完只后流要關閉,不然容易造成資源搶占,內存泄漏 try { if (inStream != null) { inStream.close(); } if (randomAccessFile != null) { randomAccessFile.close(); } if (conn != null) { conn.disconnect(); } } catch (IOException e) { e.printStackTrace(); } } } /** * 構建請求連接時的參數 返回開始下載的位置 * * @param conn */ private void constructConnection(HttpURLConnection conn) throws IOException { conn.setConnectTimeout(5 * 1000);// 設置連接超時5秒 conn.setRequestMethod("GET");// GET方式提交,如果你是用post請求必須添加 conn.setDoOutput(true); conn.setDoInput(true); conn.setRequestProperty( "Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*"); conn.setRequestProperty("Accept-Language", "zh-CN"); conn.setRequestProperty("Referer", this.url); conn.setRequestProperty("Charset", "UTF-8"); int startPositionNew = this.startPostion + this.compeletedSize; // 設置獲取實體數據的范圍 conn.setRequestProperty("Range", "bytes=" + startPositionNew + "-" + this.endPostion); conn.setRequestProperty( "User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.connect(); } } }
Github地址:https://github.com/Shanlovana/DownLoadFiles/
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。