什么是斷點續(xù)傳?
客戶端軟件斷點續(xù)傳指的是在下載或上傳時,將下載或上傳任務(wù)(一個文件或一個壓縮包)人為的劃分為幾個部分,每一個部分采用一個線程進行上傳或下載,如果碰到網(wǎng)絡(luò)故障,可以從已經(jīng)上傳或下載的部分開始繼續(xù)上傳下載未完成的部分,而沒有必要從頭開始上傳下載。用戶可以節(jié)省時間,節(jié)省流量,也提高速度。
我寫了個小Demo,先看下實現(xiàn)效果圖:



斷點續(xù)傳的原理?
斷點續(xù)傳其實并沒有那么神秘,它只不過是利用了HTTP傳輸協(xié)議中請求頭(REQUEST HEADER)的不同來進行斷點續(xù)傳的。
以下是我剛在百度音樂下載MP3的時候用Firebug截下來的HTTP請求頭:
Accept:image/webp,*/*;q=0.8Accept-Encoding:gzip, deflate, sdchAccept-Language:zh-CN,zh;q=0.8Connection:keep-aliveCookie:BIDUPSID=6B8AF721169ED82B182A7EE22F75BB87; BAIDUID=6B8AF721169ED82B182A7EE22F75BB87:FG=1; BDUSS=1pWS14dzl6Ry02MVJoN0toT1RlTzRIdkdBRVlsN1JJdG9OVmQ5djAybTJ1a1JWQVFBQUFBJCQAAAAAAAAAAAEAAACkSkgjTXJfTGVlX-fiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALYtHVW2LR1VaW; __xsptplus188=188.1.1428024707.1428024712.2%234%7C%7C%7C%7C%7C%23%23QdAfR9H5KZHSIGakiCWebLQCd6CjKjz5%23; locale=zh; cflag=65279%3A3; BDRCVFR[-z8N-kPXoJt]=I67x6TjHwwYf0; H_PS_PSSID=11099_13386_1439_13425_13075_10902_12953_12868_13320_12691_13410_12722_12737_13085_13325_13203_12835_13161_8498Host:b.hiphotos.baidu.comReferer:http://music.baidu.com/song/124380645User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
這是百度音樂服務(wù)器給我返回的信息:
Age:347651Cache-Control:max-age=31536000Connection:keep-aliveContent-Length:16659Content-Type:image/jpegDate:Sat, 11 APR 2015 06:48:45 GMTError-Message:OKETag:"10487865676202532488"Expires:Wed, 06 Apr 2016 06:02:50 GMTLast-Modified:Tue, 07 Apr 2015 05:45:32 GMTOhc-Cachable:1Server:jsp3/2.0.7
這是一般的下載請求,服務(wù)器會給我們返回我們請求下載文件的長度(Content-Length),成功狀態(tài)碼為:200。
而斷點續(xù)傳是要從我們之前已經(jīng)下載好的文件位置繼續(xù)上一次的傳輸,顧名思義我們需要告訴服務(wù)器我們上一次已經(jīng)下載到哪里了,所以在我們的HTTP請求頭里要額外的包含一條信息,而這也就是斷點續(xù)傳的奧秘所在了。
這條信息為:(RANGE為請求頭屬性,X為從什么地方開始,Y為到什么地方結(jié)束)
RANGE: bytes=X-Y
例如:
Range : 用于客戶端到服務(wù)器端的請求,可通過該字段指定下載文件的某一段大小,及其單位。典型的格式如:Range: bytes=0-499 下載第0-499字節(jié)范圍的內(nèi)容Range: bytes=500-999 下載第500-999字節(jié)范圍的內(nèi)容Range: bytes=-500 下載最后500字節(jié)的內(nèi)容Range: bytes=500- 下載從第500字節(jié)開始到文件結(jié)束部分的內(nèi)容
以上是HTTP需要注意的地方,接下來講講java里需要注意的幾個類
關(guān)于這個HTTP請求,在Java的Net包下,給我們封裝好了一系列的類,我們直接拿來使用就行了。

這是java.net.URLConnection類下的一個方法,用來設(shè)置HTTP請求頭的,Key對應(yīng)HTTP請求頭屬性,Value對應(yīng)請求頭屬性的值。


這是java.io.RandomaccessFile類,這個類的實例支持對隨機訪問文件的讀取和寫入,這個類里有個很重要的seek(long pos)方法,
這個方法可以在你設(shè)置的長度的下一個位置進行寫入,例如seek(599),那么系統(tǒng)下一次寫入的位置就是該文件的600位置。
既然是斷點續(xù)傳,那么我們自身肯定也要記錄斷點位置,所以這里數(shù)據(jù)庫也是必不可少的。
具體實現(xiàn):
好了,了解了上面的知識點后,可以開始動手寫程序了。
下面是我寫的一個小Demo,用單線程來實現(xiàn)的斷點續(xù)傳的,注釋非常全。
這是Demo的結(jié)構(gòu)圖:(實在不會畫圖,湊合著看哈)

主類(Activity):
1 package com.example.ui; 2 3 import android.app.Activity; 4 import android.app.AlertDialog; 5 import android.app.ProgressDialog; 6 import android.app.AlertDialog.Builder; 7 import android.content.BroadcastReceiver; 8 import android.content.Context; 9 import android.content.DialogInterface; 10 import android.content.Intent; 11 import android.content.IntentFilter; 12 import android.os.Bundle; 13 import android.util.Log; 14 import android.view.View; 15 import android.view.View.OnClickListener; 16 import android.widget.Button; 17 import android.widget.ProgressBar; 18 import android.widget.TextView; 19 20 import com.example.downloadfiletest.R; 21 import com.example.entity.FileInfo; 22 import com.example.logic.DownloadService; 23 24 public class MainActivity extends Activity { 25 26 private TextView textView; 27 private ProgressBar progressBar; 28 private Button bt_start; 29 private Button bt_stop; 30 31 @Override 32 protected void onCreate(Bundle savedInstanceState) { 33 super.onCreate(savedInstanceState); 34 setContentView(R.layout.activity_main); 35 initView(); // 初始化控件 36 37 // 注冊廣播接收者 38 IntentFilter intentFilter = new IntentFilter(); 39 intentFilter.addAction(DownloadService.UPDATE); 40 registerReceiver(broadcastReceiver, intentFilter); 41 42 } 43 44 @Override 45 protected void onDestroy() { 46 super.onDestroy(); 47 // 解綁 48 unregisterReceiver(broadcastReceiver); 49 } 50 51 // 初始化控件 52 private void initView() { 53 textView = (TextView) findViewById(R.id.textView); 54 progressBar = (ProgressBar) findViewById(R.id.progressBar); 55 bt_start = (Button) findViewById(R.id.bt_start); 56 bt_stop = (Button) findViewById(R.id.bt_stop); 57 progressBar.setMax(100); 58 59 final FileInfo fileInfo = new FileInfo(0, "Best Of Joy.MP3-Michael Jackson", "http://music.baidu.com/data/music/file?link=http://yinyueshiting.baidu.com/data2/music/38264529/382643441428735661128.mp3?xcode=46e7c02e3acba184b6145f688bb9f2422c866f9e4969f410&song_id=38264344", 0, 0); 60 61 // 點擊開始下載 62 bt_start.setOnClickListener(new OnClickListener() { 63 64 @Override 65 public void onClick(View v) { 66 Intent intent = new Intent(MainActivity.this, DownloadService.class); 67 intent.setAction(DownloadService.START); 68 intent.putExtra("FileInfo", fileInfo); 69 startService(intent); 70 textView.setText("正在下載文件:" + fileInfo.getFileName()); 71 72 } 73 }); 74 75 // 點擊停止下載 76 bt_stop.setOnClickListener(new OnClickListener() { 77 78 @Override 79 public void onClick(View v) { 80 Intent intent = new Intent(MainActivity.this, DownloadService.class); 81 intent.setAction(DownloadService.STOP); 82 intent.putExtra("FileInfo", fileInfo); 83 startService(intent); 84 textView.setText("任務(wù)已暫停,請點擊下載繼續(xù)"); 85 } 86 }); 87 88 } 89 90 // 廣播接收者 91 BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { 92 93 @Override 94 public void onReceive(Context context, Intent intent) { 95 if (intent.getAction().equals(DownloadService.UPDATE)) { 96 int finished = intent.getIntExtra("finished", 0); 97 progressBar.setProgress(finished); 98 99 //用戶界面友好,提醒用戶任務(wù)下載完成100 if (finished ==100) {101 AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this);102 builder.setTitle("任務(wù)狀態(tài)");103 builder.setMessage("文件下載已完成!");104 builder.setPositiveButton("確認(rèn)", new DialogInterface.OnClickListener() {105 106 @Override107 public void onClick(DialogInterface dialog, int which) {108 progressBar.setProgress(0);109 textView.setText("請點擊下載");110 }111 });112 builder.show();113 }114 }115 }116 };117 }后臺服務(wù)類(Service)
1 package com.example.logic; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.io.RandomAccessFile; 6 import java.net.HttpURLConnection; 7 import java.net.URL; 8 9 import org.apache.http.HttpStatus; 10 import org.apache.http.client.ClientProtocolException; 11 12 import android.app.Service; 13 import android.content.Intent; 14 import android.os.Environment; 15 import android.os.Handler; 16 import android.os.IBinder; 17 import android.util.Log; 18 19 import com.example.entity.FileInfo; 20 21 public class DownloadService extends Service { 22 23 // 按鈕標(biāo)志符 24 public static final String START = "START"; 25 public static final String STOP = "STOP"; 26 // 更新進度標(biāo)志 27 public static final String UPDATE = "UPDATE"; 28 // 下載路徑(內(nèi)存卡(SD)根目錄下的/downloads/) 29 public static final String DOWNLOADPATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/downloads/"; 30 // 定義初始化文件操作標(biāo)志 31 public static final int INIT = 0; 32 33 private DownloadTask downloadTask; 34 35 private Handler handler = new Handler() { 36 public void handleMessage(android.os.Message msg) { 37 switch (msg.what) { 38 case INIT: 39 FileInfo fileInfo = (FileInfo) msg.obj; 40 Log.i("init", fileInfo.toString()); 41 // 進行下載任務(wù)操作 42 downloadTask = new DownloadTask(DownloadService.this, fileInfo); 43 downloadTask.download(); 44 break; 45 } 46 }; 47 }; 48 49 /** 50 * 當(dāng)Service啟動時會被調(diào)用,用來接收Activity傳送過來的數(shù)據(jù) 51 */ 52 @Override 53 public int onStartCommand(Intent intent, int flags, int startId) { 54 if (intent.getAction().equals(START)) { 55 // 當(dāng)點擊開始下載操作時 56 // 接收Activity(putExtra)過來的數(shù)據(jù) 57 FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("FileInfo"); 58 Log.i(START, fileInfo.toString()); 59 new Thread(new InitFileThread(fileInfo)).start(); 60 61 } else if (intent.getAction().equals(STOP)) { 62 // 當(dāng)點擊停止下載操作時 63 FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("FileInfo"); 64 Log.i(STOP, fileInfo.toString()); 65 // 暫定任務(wù) 66 if (downloadTask != null) { 67 downloadTask.flag = true; 68 } 69 } 70 71 return super.onStartCommand(intent, flags, startId); 72 } 73 74 @Override 75 public IBinder onBind(Intent intent) { 76 return null; 77 } 78 79 // 初始化文件操作獲取網(wǎng)絡(luò)資源大小長度,開辟子線程 80 class InitFileThread implements Runnable { 81 82 private FileInfo fileInfo; 83 84 // 構(gòu)造方法,獲取文件對象 85 public InitFileThread(FileInfo fileInfo) { 86 this.fileInfo = fileInfo; 87 } 88 89 @Override 90 public void run() { 91 /* 92 * 1、打開網(wǎng)絡(luò)連接,獲取文件長度 2、創(chuàng)建本地文件,長度和網(wǎng)絡(luò)文件相等 93 */ 94 HttpURLConnection httpURLConnection = null; 95 RandomAccessFile randomAccessFile = null; 96 try { 97 URL url = new URL(fileInfo.getUrl()); 98 httpURLConnection = (HttpURLConnection) url.openConnection(); 99 // 知識點:除了下載文件,其他一律用POST100 httpURLConnection.setConnectTimeout(3000);101 httpURLConnection.setRequestMethod("GET");102 // 定義文件長度103 int length = -1;104 // 網(wǎng)絡(luò)連接成功105 if (httpURLConnection.getResponseCode() == HttpStatus.SC_OK) {106 length = httpURLConnection.getContentLength();107 }108 // 判斷是否取得文件長度109 if (length <= 0) {110 return;111 }112 113 // 創(chuàng)建文件目錄對象114 File dir = new File(DOWNLOADPATH);115 if (!dir.exists()) {116 // 若目錄不存在,創(chuàng)建117 dir.mkdir();118 }119 // 創(chuàng)建文件對象120 File file = new File(dir, fileInfo.getFileName());121 // 創(chuàng)建隨機訪問文件流 參數(shù)二為權(quán)限:讀寫刪122 randomAccessFile = new RandomAccessFile(file, "rwd");123 randomAccessFile.setLength(length);124 fileInfo.setLength(length);125 // 發(fā)送handler126 handler.obtainMessage(INIT, fileInfo).sendToTarget();127 128 } catch (ClientProtocolException e) {129 e.printStackTrace();130 } catch (IOException e) {131 e.printStackTrace();132 } finally {133 if (randomAccessFile != null) {134 try {135 randomAccessFile.close();136 } catch (IOException e) {137 e.printStackTrace();138 }139 }140 }141 142 }143 144 }145 146 }下載任務(wù)類:
1 package com.example.logic; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.RandomAccessFile; 7 import java.net.HttpURLConnection; 8 import java.net.MalformedURLException; 9 import java.net.URL; 10 import java.util.List; 11 12 import org.apache.http.HttpStatus; 13 14 import android.content.Context; 15 import android.content.Intent; 16 import android.util.Log; 17 18 import com.example.dao.ThreadDAO; 19 import com.example.dao.ThreadDAOImpl; 20 import com.example.entity.FileInfo; 21 import com.example.entity.ThreadInfo; 22 23 /** 24 * 下載任務(wù)類 25 * 26 * @author Balla_兔子 27 * 28 */ 29 public class DownloadTask { 30 private Context context; 31 private ThreadDAO dao; 32 private FileInfo fileInfo; 33 // 初始化下載進度,默認(rèn)為0 34 private int finished = 0; 35 36 // 是否暫停下載標(biāo)識符 37 public boolean flag = false; 38 39 public DownloadTask(Context context, FileInfo fileInfo) { 40 this.context = context; 41 this.fileInfo = fileInfo; 42 dao = new ThreadDAOImpl(context); 43 } 44 45 public void download() { 46 // 線程信息的url和文件的url對應(yīng) 47 List<ThreadInfo> threadInfos = dao.getThreadInfo(fileInfo.getUrl()); 48 ThreadInfo threadInfo = null; 49 if (threadInfos.size() == 0) { 50 // 若數(shù)據(jù)庫無此線程任務(wù) 51 threadInfo = new ThreadInfo(0, fileInfo.getUrl(), 0, fileInfo.getLength(), 0); 52 } else { 53 threadInfo = threadInfos.get(0); 54 } 55 // 創(chuàng)建子線程進行下載 56 new Thread(new DownloadThread(threadInfo)).start(); 57 } 58 59 // 執(zhí)行下載任務(wù),開辟子線程 60 class DownloadThread implements Runnable { 61 62 private ThreadInfo threadInfo; 63 64 public DownloadThread(ThreadInfo threadInfo) { 65 this.threadInfo = threadInfo; 66 } 67 68 @Override 69 public void run() { 70 HttpURLConnection urlConnection = null; 71 InputStream inputStream = null; 72 RandomAccessFile randomAccessFile = null; 73 74 /** 75 * 執(zhí)行下載任務(wù) 76 * 1、查詢數(shù)據(jù)庫,確定是否已存在此下載線程,便于繼續(xù)下載 77 * 2、設(shè)置從哪個位置開始下載 78 * 3、設(shè)置文件的寫入位置 79 * 4、開始下載 80 * 5、廣播通知UI更新下載進度 81 * 6、暫停線程的操作 82 * 7、下載完畢,刪除數(shù)據(jù)庫信息 83 */ 84 // 1、查詢數(shù)據(jù)庫 85 if (!dao.isExists(threadInfo.getUrl(), threadInfo.getThread_id())) { 86 // 若不存在,插入新線程信息 87 dao.insertThread(threadInfo); 88 } 89 90 // 2、設(shè)置下載位置 91 try { 92 URL url = new URL(threadInfo.getUrl()); 93 urlConnection = (HttpURLConnection) url.openConnection(); 94 // 設(shè)置連接超時時間 95 urlConnection.setConnectTimeout(3000); 96 urlConnection.setRequestMethod("GET"); 97 98 // 設(shè)置請求屬性 99 // 參數(shù)一:Range頭域可以請求實體的一個或者多個子范圍(一半用于斷點續(xù)傳),如果用戶的請求中含有range100 // ,則服務(wù)器的相應(yīng)代碼為206。101 // 參數(shù)二:表示請求的范圍:比如頭500個字節(jié):bytes=0-499102 103 // 獲取線程已經(jīng)下載的進度104 int start = threadInfo.getStart() + threadInfo.getFinished();105 urlConnection.setRequestProperty("range", "bytes=" + start + "-" + threadInfo.getEnd());106 107 // 3、設(shè)置文件的寫入位置108 File file = new File(DownloadService.DOWNLOADPATH, fileInfo.getFileName());109 randomAccessFile = new RandomAccessFile(file, "rwd");110 // 設(shè)置從哪里開始寫入,如參數(shù)為100,那就從101開始寫入111 randomAccessFile.seek(start);112 113 finished += threadInfo.getFinished();114 Intent intent = new Intent(DownloadService.UPDATE);115 // 4、開始下載116 if (urlConnection.getResponseCode() == HttpStatus.SC_PARTIAL_CONTENT) {117 inputStream = urlConnection.getInputStream();118 // 設(shè)置字節(jié)數(shù)組緩沖區(qū)119 byte[] data = new byte[1024*4];120 // 讀取長度121 int len = -1;122 // 取得當(dāng)前時間123 long time = System.currentTimeMillis();124 while ((len = inputStream.read(data)) != -1) {125 // 讀取成功,寫入文件126 randomAccessFile.write(data, 0, len);127 128 // 避免更新過快,減緩主線程壓力,讓其0.5秒發(fā)送一次進度129 if (System.currentTimeMillis() - time > 500) {130 time = System.currentTimeMillis();131 // 把當(dāng)前進度通過廣播傳遞給UI132 finished += len;133 Log.i("finished:", finished+"");134 Log.i("file:", fileInfo.getLength()+"");135 intent.putExtra("finished", finished*100 / fileInfo.getLength());136 context.sendBroadcast(intent);137 }138 139 if (flag) {140 // 暫停下載,更新進度到數(shù)據(jù)庫141 dao.updateThread(threadInfo.getUrl(), threadInfo.getThread_id(), finished);142 // 結(jié)束線程143 return;144 }145 146 }147 // 當(dāng)下載執(zhí)行完畢時,刪除數(shù)據(jù)庫線程信息148 dao.deleteThread(threadInfo.getUrl(), threadInfo.getThread_id());149 }150 151 } catch (MalformedURLException e) {152 e.printStackTrace();153 } catch (IOException e) {154 e.printStackTrace();155 } finally {156 if (urlConnection != null) {157 urlConnection.disconnect();158 }159 if (inputStream != null) {160 try {161 inputStream.close();162 } catch (IOException e) {163 e.printStackTrace();164 }165 }166 if (randomAccessFile != null) {167 try {168 randomAccessFile.close();169 } catch (IOException e) {170 e.printStackTrace();171 }172 }173 174 }175 176 }177 }178 }數(shù)據(jù)庫幫助類:
1 package com.example.db; 2 3 import android.content.Context; 4 import android.database.sqlite.SQLiteDatabase; 5 import android.database.sqlite.SQLiteOpenHelper; 6 7 public class DBHelper extends SQLiteOpenHelper { 8 public static final String DBNAME = "download.db"; 9 public static final int VERSION = 1;10 public static final String TABLE="threadinfo";11 public static final String CREATE_DB = "create table threadinfo (_id integer primary key autoincrement,thread_id integer,url text,start integer,end integer,finished integer) ";12 public static final String DROP_DB = "drop table if exists threadinfo";13 14 public DBHelper(Context context) {15 super(context, DBNAME, null, VERSION);16 }17 18 @Override19 public void onCreate(SQLiteDatabase db) {20 db.execSQL(CREATE_DB);21 22 }23 24 @Override25 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {26 db.execSQL(DROP_DB);27 db.execSQL(CREATE_DB);28 }29 30 }數(shù)據(jù)庫表接口類:
1 package com.example.dao; 2 3 import java.util.List; 4 5 import com.example.entity.ThreadInfo; 6 7 public interface ThreadDAO { 8 // 新增一條線程信息 9 public void insertThread(ThreadInfo threadInfo);10 11 // 刪除一條線程信息(多線程下載,可能一個url對應(yīng)多個線程,所以需要2個條件)12 public void deleteThread(String url, int thread_id);13 14 // 修改一條線程信息15 public void updateThread(String url, int thread_id, int finished);16 17 // 查詢線程有關(guān)信息(根據(jù)url查詢下載該url的所有線程信息)18 public List<ThreadInfo> getThreadInfo(String url);19 20 // 判斷線程是否已經(jīng)存在21 public boolean isExists(String url, int thread_id);22 23 24 }數(shù)據(jù)庫表接口實現(xiàn)類:
1 package com.example.dao; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import android.content.ContentValues; 7 import android.content.Context; 8 import android.database.Cursor; 9 import android.database.sqlite.SQLiteDatabase;10 11 import com.example.db.DBHelper;12 import com.example.entity.ThreadInfo;13 14 /**15 * ThreadDAO接口實現(xiàn)類16 * 17 * @author Balla_兔子18 * 19 */20 public class ThreadDAOImpl implements ThreadDAO {21 22 private DBHelper dbHelper;23 24 public ThreadDAOImpl(Context context) {25 dbHelper = new DBHelper(context);26 }27 28 @Override29 public void insertThread(ThreadInfo threadInfo) {30 SQLiteDatabase db = dbHelper.getWritableDatabase();31 ContentValues values = new ContentValues();32 values.put("thread_id", threadInfo.getThread_id());33 values.put("url ", threadInfo.getUrl());34 values.put("start ", threadInfo.getStart());35 values.put("end ", threadInfo.getEnd());36 values.put("finished ", threadInfo.getFinished());37 db.insert(DBHelper.TABLE, null, values);38 db.close();39 }40 41 @Override42 public void deleteThread(String url, int thread_id) {43 SQLiteDatabase db = dbHelper.getWritableDatabase();44 db.delete(DBHelper.TABLE, "url=? and thread_id=?", new String[] { url, String.valueOf(thread_id) });45 db.close();46 }47 48 @Override49 public void updateThread(String url, int thread_id, int finished) {50 SQLiteDatabase db = dbHelper.getWritableDatabase();51 db.execSQL("update threadinfo set finished = ? where url = ? and thread_id=?", new Object[] { finished, url, thread_id });52 db.close();53 }54 55 @Override56 public List<ThreadInfo> getThreadInfo(String url) {57 List<ThreadInfo> list = new ArrayList<ThreadInfo>();58 SQLiteDatabase db = dbHelper.getWritableDatabase();59 Cursor cursor = db.query(DBHelper.TABLE, null, "url=?", new String[] { url }, null, null, null);60 while (cursor.moveToNext()) {61 ThreadInfo threadInfo = new ThreadInfo();62 threadInfo.setThread_id(cursor.getInt(cursor.getColumnIndex("thread_id")));63 threadInfo.setUrl(cursor.getString(cursor.getColumnIndex("url")));64 threadInfo.setStart(cursor.getInt(cursor.getColumnIndex("start")));65 threadInfo.setEnd(cursor.getInt(cursor.getColumnIndex("end")));66 threadInfo.setFinished(cursor.getInt(cursor.getColumnIndex("finished")));67 list.add(threadInfo);68 }69 cursor.close();70 db.close();71 return list;72 }73 74 @Override75 public boolean isExists(String url, int thread_id) {76 SQLiteDatabase db = dbHelper.getWritableDatabase();77 Cursor cursor = db.query(DBHelper.TABLE, null, "url=? and thread_id=?", new String[] { url, String.valueOf(thread_id) }, null, null, null);78 boolean isExists = cursor.moveToNext();79 db.close();80 return isExists;81 }82 83 }實體類:(文件,線程)
文件實體類:

1 package com.example.entity; 2 3 import java.io.Serializable; 4 5 public class FileInfo implements Serializable { 6 7 private int id; 8 private String fileName; 9 private String url;10 private int length;11 private int finished;12 13 public FileInfo() {14 }15 16 public FileInfo(int id, String fileName, String url, int length,17 int finished) {18 super();19 this.id = id;20 this.fileName = fileName;21 this.url = url;22 this.length = length;23 this.finished = finished;24 }25 26 public int getId() {27 return id;28 }29 30 public void setId(int id) {31 this.id = id;32 }33 34 public String getFileName() {35 return fileName;36 }37 38 public void setFileName(String fileName) {39 this.fileName = fileName;40 }41 42 public String getUrl() {43 return url;44 }45 46 public void setUrl(String url) {47 this.url = url;48 }49 50 public int getLength() {51 return length;52 }53 54 public void setLength(int length) {55 this.length = length;56 }57 58 public int getFinished() {59 return finished;60 }61 62 public void setFinished(int finished) {63 this.finished = finished;64 }65 66 @Override67 public String toString() {68 return "FileInfo [id=" + id + ", fileName=" + fileName + ", url=" + url69 + ", length=" + length + ", finished=" + finished + "]";70 }71 72 }View Code線程實體類:

1 package com.example.entity; 2 3 import java.io.Serializable; 4 5 public class ThreadInfo implements Serializable { 6 7 private int thread_id; 8 private String url; 9 private int start;10 private int end;11 private int finished;12 13 public ThreadInfo() {14 }15 16 public ThreadInfo(int thread_id, String url, int start, int end, int finished) {17 super();18 this.thread_id = thread_id;19 this.url = url;20 this.start = start;21 this.end = end;22 this.finished = finished;23 }24 25 public int getThread_id() {26 return thread_id;27 }28 29 public void setThread_id(int thread_id) {30 this.thread_id = thread_id;31 }32 33 public String getUrl() {34 return url;35 }36 37 public void setUrl(String url) {38 this.url = url;39 }40 41 public int getStart() {42 return start;43 }44 45 public void setStart(int start) {46 this.start = start;47 }48 49 public int getEnd() {50 return end;51 }52 53 public void setEnd(int end) {54 this.end = end;55 }56 57 public int getFinished() {58 return finished;59 }60 61 public void setFinished(int finished) {62 this.finished = finished;63 }64 65 @Override66 public String toString() {67 return "ThreadInfo [thread_id=" + thread_id + ", url=" + url + ", start=" + start + ", end=" + end + ", finished=" + finished + "]";68 }69 70 }View Code關(guān)于多線程的斷點續(xù)傳,其實原理差不多,還是一樣把一個文件分成不同區(qū)域,然后每個區(qū)域各用一條線程去執(zhí)行下載任務(wù),然后再把文件合并,改天有時間再上個Demo給大家看吧。

作者:Balla_兔子出處:http://m.survivalescaperooms.com/lichenwei/本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,日后必有一番作為!旁邊有“推薦”二字,你就順手把它點了吧,相得準(zhǔn),我分文不收;相不準(zhǔn),你也好回來找我!
新聞熱點
疑難解答