什麼是斷點續傳?java
客戶端軟件斷點續傳指的是在下載或上傳時,將下載或上傳任務(一個文件或一個壓縮包)人爲的劃分爲幾個部分,每個部分採用一個線程進行上傳或下載,若是碰到網絡故障,能夠從已經上傳或下載的部分開始繼續上傳下載未完成的部分,而沒有必要從頭開始上傳下載。用戶能夠節省時間,節省流量,也提升速度。android
我寫了個小Demo,先看下實現效果圖:web
斷點續傳的原理?sql
斷點續傳其實並無那麼神祕,它只不過是利用了HTTP傳輸協議中請求頭(REQUEST HEADER)的不一樣來進行斷點續傳的。數據庫
如下是我剛在百度音樂下載MP3的時候用Firebug截下來的HTTP請求頭:apache
Accept:image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch
Accept-Language:zh-CN,zh;q=0.8
Connection:keep-alive
Cookie: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_8498
Host:b.hiphotos.baidu.com
Referer:http://music.baidu.com/song/124380645
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
這是百度音樂服務器給我返回的信息:數組
Age:347651
Cache-Control:max-age=31536000
Connection:keep-alive
Content-Length:16659
Content-Type:image/jpeg
Date:Sat, 11 Apr 2015 06:48:45 GMT
Error-Message:OK
ETag:"10487865676202532488"
Expires:Wed, 06 Apr 2016 06:02:50 GMT
Last-Modified:Tue, 07 Apr 2015 05:45:32 GMT
Ohc-Cachable:1
Server:JSP3/2.0.7
這是通常的下載請求,服務器會給咱們返回咱們請求下載文件的長度(Content-Length),成功狀態碼爲:200。xcode
而斷點續傳是要從咱們以前已經下載好的文件位置繼續上一次的傳輸,顧名思義咱們須要告訴服務器咱們上一次已經下載到哪裏了,因此在咱們的HTTP請求頭裏要額外的包含一條信息,而這也就是斷點續傳的奧祕所在了。服務器
這條信息爲:(RANGE爲請求頭屬性,X爲從什麼地方開始,Y爲到什麼地方結束)網絡
RANGE: bytes=X-Y
例如:
Range : 用於客戶端到服務器端的請求,可經過該字段指定下載文件的某一段大小,及其單位。典型的格式如:
Range: bytes=0-499 下載第0-499字節範圍的內容
Range: bytes=500-999 下載第500-999字節範圍的內容
Range: bytes=-500 下載最後500字節的內容
Range: bytes=500- 下載從第500字節開始到文件結束部分的內容
以上是HTTP須要注意的地方,接下來說講Java裏須要注意的幾個類
關於這個HTTP請求,在Java的Net包下,給咱們封裝好了一系列的類,咱們直接拿來使用就好了。
這是java.net.URLConnection類下的一個方法,用來設置HTTP請求頭的,Key對應HTTP請求頭屬性,Value對應請求頭屬性的值。
這是java.io.RandomAccessFile類,這個類的實例支持對隨機訪問文件的讀取和寫入,這個類裏有個很重要的seek(long pos)方法,
這個方法能夠在你設置的長度的下一個位置進行寫入,例如seek(599),那麼系統下一次寫入的位置就是該文件的600位置。
既然是斷點續傳,那麼咱們自身確定也要記錄斷點位置,因此這裏數據庫也是必不可少的。
具體實現:
好了,瞭解了上面的知識點後,能夠開始動手寫程序了。
下面是我寫的一個小Demo,用單線程來實現的斷點續傳的,註釋很是全。
這是Demo的結構圖:(實在不會畫圖,湊合着看哈)
主類(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("任務已暫停,請點擊下載繼續"); 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 //用戶界面友好,提醒用戶任務下載完成 100 if (finished ==100) { 101 AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this); 102 builder.setTitle("任務狀態"); 103 builder.setMessage("文件下載已完成!"); 104 builder.setPositiveButton("確認", new DialogInterface.OnClickListener() { 105 106 @Override 107 public void onClick(DialogInterface dialog, int which) { 108 progressBar.setProgress(0); 109 textView.setText("請點擊下載"); 110 } 111 }); 112 builder.show(); 113 } 114 } 115 } 116 }; 117 }
後臺服務類(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 // 按鈕標誌符 24 public static final String START = "START"; 25 public static final String STOP = "STOP"; 26 // 更新進度標誌 27 public static final String UPDATE = "UPDATE"; 28 // 下載路徑(內存卡(SD)根目錄下的/downloads/) 29 public static final String DOWNLOADPATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/downloads/"; 30 // 定義初始化文件操做標誌 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 // 進行下載任務操做 42 downloadTask = new DownloadTask(DownloadService.this, fileInfo); 43 downloadTask.download(); 44 break; 45 } 46 }; 47 }; 48 49 /** 50 * 當Service啓動時會被調用,用來接收Activity傳送過來的數據 51 */ 52 @Override 53 public int onStartCommand(Intent intent, int flags, int startId) { 54 if (intent.getAction().equals(START)) { 55 // 當點擊開始下載操做時 56 // 接收Activity(putExtra)過來的數據 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 // 當點擊中止下載操做時 63 FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("FileInfo"); 64 Log.i(STOP, fileInfo.toString()); 65 // 暫定任務 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 // 初始化文件操做獲取網絡資源大小長度,開闢子線程 80 class InitFileThread implements Runnable { 81 82 private FileInfo fileInfo; 83 84 // 構造方法,獲取文件對象 85 public InitFileThread(FileInfo fileInfo) { 86 this.fileInfo = fileInfo; 87 } 88 89 @Override 90 public void run() { 91 /* 92 * 一、打開網絡鏈接,獲取文件長度 二、建立本地文件,長度和網絡文件相等 93 */ 94 HttpURLConnection httpURLConnection = null; 95 RandomAccessFile randomAccessFile = null; 96 try { 97 URL url = new URL(fileInfo.getUrl()); 98 httpURLConnection = (HttpURLConnection) url.openConnection(); 99 // 知識點:除了下載文件,其餘一概用POST 100 httpURLConnection.setConnectTimeout(3000); 101 httpURLConnection.setRequestMethod("GET"); 102 // 定義文件長度 103 int length = -1; 104 // 網絡鏈接成功 105 if (httpURLConnection.getResponseCode() == HttpStatus.SC_OK) { 106 length = httpURLConnection.getContentLength(); 107 } 108 // 判斷是否取得文件長度 109 if (length <= 0) { 110 return; 111 } 112 113 // 建立文件目錄對象 114 File dir = new File(DOWNLOADPATH); 115 if (!dir.exists()) { 116 // 若目錄不存在,建立 117 dir.mkdir(); 118 } 119 // 建立文件對象 120 File file = new File(dir, fileInfo.getFileName()); 121 // 建立隨機訪問文件流 參數二爲權限:讀寫刪 122 randomAccessFile = new RandomAccessFile(file, "rwd"); 123 randomAccessFile.setLength(length); 124 fileInfo.setLength(length); 125 // 發送handler 126 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 }
下載任務類:
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 * 下載任務類 25 * 26 * @author Balla_兔子 27 * 28 */ 29 public class DownloadTask { 30 private Context context; 31 private ThreadDAO dao; 32 private FileInfo fileInfo; 33 // 初始化下載進度,默認爲0 34 private int finished = 0; 35 36 // 是否暫停下載標識符 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對應 47 List<ThreadInfo> threadInfos = dao.getThreadInfo(fileInfo.getUrl()); 48 ThreadInfo threadInfo = null; 49 if (threadInfos.size() == 0) { 50 // 若數據庫無此線程任務 51 threadInfo = new ThreadInfo(0, fileInfo.getUrl(), 0, fileInfo.getLength(), 0); 52 } else { 53 threadInfo = threadInfos.get(0); 54 } 55 // 建立子線程進行下載 56 new Thread(new DownloadThread(threadInfo)).start(); 57 } 58 59 // 執行下載任務,開闢子線程 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 * 執行下載任務 76 * 一、查詢數據庫,肯定是否已存在此下載線程,便於繼續下載 77 * 二、設置從哪一個位置開始下載 78 * 三、設置文件的寫入位置 79 * 四、開始下載 80 * 五、廣播通知UI更新下載進度 81 * 六、暫停線程的操做 82 * 七、下載完畢,刪除數據庫信息 83 */ 84 // 一、查詢數據庫 85 if (!dao.isExists(threadInfo.getUrl(), threadInfo.getThread_id())) { 86 // 若不存在,插入新線程信息 87 dao.insertThread(threadInfo); 88 } 89 90 // 二、設置下載位置 91 try { 92 URL url = new URL(threadInfo.getUrl()); 93 urlConnection = (HttpURLConnection) url.openConnection(); 94 // 設置鏈接超時時間 95 urlConnection.setConnectTimeout(3000); 96 urlConnection.setRequestMethod("GET"); 97 98 // 設置請求屬性 99 // 參數一:Range頭域能夠請求實體的一個或者多個子範圍(一半用於斷點續傳),若是用戶的請求中含有range 100 // ,則服務器的相應代碼爲206。 101 // 參數二:表示請求的範圍:好比頭500個字節:bytes=0-499 102 103 // 獲取線程已經下載的進度 104 int start = threadInfo.getStart() + threadInfo.getFinished(); 105 urlConnection.setRequestProperty("range", "bytes=" + start + "-" + threadInfo.getEnd()); 106 107 // 三、設置文件的寫入位置 108 File file = new File(DownloadService.DOWNLOADPATH, fileInfo.getFileName()); 109 randomAccessFile = new RandomAccessFile(file, "rwd"); 110 // 設置從哪裏開始寫入,如參數爲100,那就從101開始寫入 111 randomAccessFile.seek(start); 112 113 finished += threadInfo.getFinished(); 114 Intent intent = new Intent(DownloadService.UPDATE); 115 // 四、開始下載 116 if (urlConnection.getResponseCode() == HttpStatus.SC_PARTIAL_CONTENT) { 117 inputStream = urlConnection.getInputStream(); 118 // 設置字節數組緩衝區 119 byte[] data = new byte[1024*4]; 120 // 讀取長度 121 int len = -1; 122 // 取得當前時間 123 long time = System.currentTimeMillis(); 124 while ((len = inputStream.read(data)) != -1) { 125 // 讀取成功,寫入文件 126 randomAccessFile.write(data, 0, len); 127 128 // 避免更新過快,減緩主線程壓力,讓其0.5秒發送一次進度 129 if (System.currentTimeMillis() - time > 500) { 130 time = System.currentTimeMillis(); 131 // 把當前進度經過廣播傳遞給UI 132 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 // 暫停下載,更新進度到數據庫 141 dao.updateThread(threadInfo.getUrl(), threadInfo.getThread_id(), finished); 142 // 結束線程 143 return; 144 } 145 146 } 147 // 當下載執行完畢時,刪除數據庫線程信息 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 }
數據庫幫助類:
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 @Override 19 public void onCreate(SQLiteDatabase db) { 20 db.execSQL(CREATE_DB); 21 22 } 23 24 @Override 25 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 26 db.execSQL(DROP_DB); 27 db.execSQL(CREATE_DB); 28 } 29 30 }
數據庫表接口類:
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對應多個線程,因此須要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 // 查詢線程有關信息(根據url查詢下載該url的全部線程信息) 18 public List<ThreadInfo> getThreadInfo(String url); 19 20 // 判斷線程是否已經存在 21 public boolean isExists(String url, int thread_id); 22 23 24 }
數據庫表接口實現類:
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接口實現類 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 @Override 29 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 @Override 42 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 @Override 49 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 @Override 56 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 @Override 75 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 @Override 67 public String toString() { 68 return "FileInfo [id=" + id + ", fileName=" + fileName + ", url=" + url 69 + ", length=" + length + ", finished=" + finished + "]"; 70 } 71 72 }
線程實體類:
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 @Override 66 public String toString() { 67 return "ThreadInfo [thread_id=" + thread_id + ", url=" + url + ", start=" + start + ", end=" + end + ", finished=" + finished + "]"; 68 } 69 70 }
關於多線程的斷點續傳,其實原理差很少,仍是同樣把一個文件分紅不一樣區域,而後每一個區域各用一條線程去執行下載任務,而後再把文件合併,改天有時間再上個Demo給你們看吧。
做者:Balla_兔子
出處:http://www.cnblogs.com/lichenwei/本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文連接。正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,往後必有一番做爲!旁邊有「推薦」二字,你就順手把它點了吧,相得準,我分文不收;相不許,你也好回來找我!