一、斷點下載原理及步驟
?????對于斷點下載铝宵,就是下載的過程中打掘,都會出現(xiàn)一些異常情況华畏,導(dǎo)致下載中斷。雖說可以重新下載尊蚁,但是這對于數(shù)據(jù)比較大的來說亡笑,這是很麻煩很蛋疼的事。
原理:
?????在下載中斷的時候横朋,通過數(shù)據(jù)庫仑乌,記錄中斷的文件信息。待恢復(fù)上傳的時候琴锭,從數(shù)據(jù)庫中讀取中斷的文件信息晰甚。通過RandomAccessFile這個類可以讓文件繼續(xù)從中斷的位置上傳。
步驟:
- 獲取下載鏈接决帖,首先根據(jù)下載鏈接到數(shù)據(jù)庫查找一下是否有重復(fù)的下載任務(wù)厕九,有的話獲取數(shù)據(jù)繼續(xù)下載,沒有的話古瓤,新建任務(wù)止剖,獲取文件對象,傳給下載服務(wù)落君。
- 新建一個下載服務(wù)穿香,方便應(yīng)用退出時,能繼續(xù)在后臺下載绎速。
- 創(chuàng)建一個數(shù)據(jù)庫皮获,用來存儲程序出現(xiàn)異常中斷時,能夠及時保存下載信息纹冤,方便下次讀取繼續(xù)下載洒宝。
- 創(chuàng)建一個線程,用來獲取待下載文件的長度和執(zhí)行下載線程萌京。
- 在不斷寫文件的過程中雁歌,通過廣播刷新下載進(jìn)度。
二知残、代碼實現(xiàn)
package com.example.river.download;
import java.io.Serializable;
/**
* Created by Administrator on 2017/10/18.
*/
public class FileInfo implements Serializable{
private String fileName;
private String url;
//文件的大小
private int len;
//文件結(jié)束位置
private int finished;
private boolean isDownloading;
public FileInfo(){
}
public FileInfo(String fileName,String url){
this.fileName = fileName;
this.url = url;
}
public boolean isDownloading() {
return isDownloading;
}
public void setDownloading(boolean downloading) {
isDownloading = downloading;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getLen() {
return len;
}
public void setLen(int len) {
this.len = len;
}
public int getFinished() {
return finished;
}
public void setFinished(int finished) {
this.finished = finished;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, DownloadService.class);
if (fileInfo.isDownloading()) {
fileInfo.setDownloading(false);
start.setText("繼續(xù)");
} else {
fileInfo.setDownloading(true);
start.setText("暫停");
}
intent.setAction("start");
intent.putExtra("fileInfo", fileInfo);
startService(intent);
}
});
restart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
start.setText("暫停");
fileInfo.setDownloading(true);
Intent intent = new Intent(MainActivity.this, DownloadService.class);
fileInfo.setFinished(0);
intent.setAction("restart");
intent.putExtra("fileInfo", fileInfo);
startService(intent);
}
});
receiver = new ProgressBroadcast();
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.ProgressBroadcast");
//注冊receiver
registerReceiver(receiver, filter);
廣播更新進(jìn)度
public class ProgressBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int progress = intent.getIntExtra("finished", 0);
mProgressBar.setProgress(progress);
}
}
檢查數(shù)據(jù)庫
private FileInfo checkDB(){
DBHelper dbHelper = new DBHelper(MainActivity.this);
SQLiteDatabase db = dbHelper.getReadableDatabase();
fileInfo= dbHelper.queryData(db,"http://www.21yey.com/clientdownload/android/family.apk");
if (fileInfo.getFinished() > 0) {
mProgressBar.setProgress(fileInfo.getFinished() * 100 / fileInfo.getLen());
start.setText("繼續(xù)");
}else {
fileInfo = new FileInfo("family.apk", "http://www.21yey.com/clientdownload/android/family.apk");
}
return fileInfo;
}
后臺下載服務(wù)靠瞎,以便應(yīng)用退出可以繼續(xù)下載
public class DownloadService extends IntentService{
public static final String ACTION_START = "start";
public static final String ACTION_RESTART = "restart";
public static final String ACTION_UPDATE = "update";
public DownloadService() {
super("download");
}
@Override
protected void onHandleIntent(Intent intent) {
TaskManager task = TaskManager.getInstance();
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
if(intent.getAction().equals(ACTION_START)){
if(fileInfo.isDownloading()){
task.start(DownloadService.this,fileInfo);
}else {
task.stop();
}
}
if(intent.getAction().equals(ACTION_RESTART)){
task.restart(DownloadService.this,fileInfo);
}
}
}
任務(wù)調(diào)度
public class TaskManager {
private Map<String,FileInfo> map = new HashMap<>();
private boolean isPause;
public static class TaskHolder{
private static final TaskManager instance = new TaskManager();
}
public static TaskManager getInstance(){
return TaskHolder.instance;
}
//恢復(fù)任務(wù)
public void start(Context context,FileInfo fileInfo){
if(map.get(fileInfo.getUrl())==null){
map.put(fileInfo.getUrl(),fileInfo);
}
DownloadTask task = new DownloadTask(map.get(fileInfo.getUrl()),context);
isPause = false;
task.start();
}
public void stop(){
isPause =true;
}
public void restart(Context context,FileInfo fileInfo){
try {
map.clear();
File file = new File(DownloadTask.FILE_PATH,fileInfo.getFileName());
if (file.exists()){
file.delete();
}
Thread.sleep(100);
}catch (Exception e){
return;
}
start(context,fileInfo);
}
public boolean isPause() {
return isPause;
}
public void setPause(boolean pause) {
isPause = pause;
}
}
獲取待下載的文件長度
int length = -1;
try {
HttpURLConnection conn = null;
URL url = new URL(info.getUrl());
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
if(conn.getResponseCode() ==200){
length =conn.getContentLength();
}
if(length<0){
return;
}
File dir = new File(DownloadTask.FILE_PATH);
if(!dir.exists()){
dir.mkdir();
}
info.setLen(length);
conn.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
執(zhí)行下載任務(wù)
HttpURLConnection connection = null;
RandomAccessFile raf = null;
try {
URL urls = new URL(info.getUrl());
connection = (HttpURLConnection) urls.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(3000);
//獲取上次下載位置
int start = info.getFinished();
connection.setRequestProperty("Range","bytes="+start+"-"+length);
//設(shè)置文件寫入位置
File file = new File(FILE_PATH,info.getFileName());
raf= new RandomAccessFile(file,"rwd");
raf.seek(start);
finished += info.getFinished();
if(connection.getResponseCode() == 206){
InputStream is =connection.getInputStream();
byte[] bytes = new byte[1024*4];
int len;
while ((len = is.read(bytes))!=-1){
raf.write(bytes,0,len);
finished+=len;
info.setFinished(finished);
if(TaskManager.getInstance().isPause()){
info.setDownloading(false);
dbHelper.insert(db,info);
db.close();
return;
}
//實時更新下載進(jìn)度
Intent intent = new Intent(DownloadService.ACTION_UPDATE);
intent.putExtra("finished", finished * 100 / length);
intent.setAction("android.intent.action.ProgressBroadcast");
context.sendBroadcast(intent);
}
info.setDownloading(false);
dbHelper.insert(db,info);
db.close();
}
} catch (Exception e) {
e.printStackTrace();
}
運行效果:
這里寫圖片描述
三、總結(jié)
?????我簡單的實現(xiàn)單任務(wù)單線程的下載(適合文件較星竺谩)乏盐。后續(xù)會實現(xiàn)單任務(wù)多線程下載、多任務(wù)多線程下載(文件較大的)和上傳制恍,敬請關(guān)注父能。雖然代碼簡陋,但簡潔思路清晰比較好理解净神。我的目的不是實現(xiàn)一個高性能可擴(kuò)展的下載器何吝,而是展示具體如何實現(xiàn)下載的一個流程溉委。當(dāng)然,我實現(xiàn)的代碼都是比較基礎(chǔ)的岔霸,比較好理解薛躬。
掃描領(lǐng)紅包
1513521727541.jpg