自己動手實現(xiàn)一個Android斷點下載

一、斷點下載原理及步驟

?????對于斷點下載铝宵,就是下載的過程中打掘,都會出現(xiàn)一些異常情況华畏,導(dǎo)致下載中斷。雖說可以重新下載尊蚁,但是這對于數(shù)據(jù)比較大的來說亡笑,這是很麻煩很蛋疼的事。

原理:
?????在下載中斷的時候横朋,通過數(shù)據(jù)庫仑乌,記錄中斷的文件信息。待恢復(fù)上傳的時候琴锭,從數(shù)據(jù)庫中讀取中斷的文件信息晰甚。通過RandomAccessFile這個類可以讓文件繼續(xù)從中斷的位置上傳。

步驟:

  1. 獲取下載鏈接决帖,首先根據(jù)下載鏈接到數(shù)據(jù)庫查找一下是否有重復(fù)的下載任務(wù)厕九,有的話獲取數(shù)據(jù)繼續(xù)下載,沒有的話古瓤,新建任務(wù)止剖,獲取文件對象,傳給下載服務(wù)落君。
  2. 新建一個下載服務(wù)穿香,方便應(yīng)用退出時,能繼續(xù)在后臺下載绎速。
  3. 創(chuàng)建一個數(shù)據(jù)庫皮获,用來存儲程序出現(xiàn)異常中斷時,能夠及時保存下載信息纹冤,方便下次讀取繼續(xù)下載洒宝。
  4. 創(chuàng)建一個線程,用來獲取待下載文件的長度和執(zhí)行下載線程萌京。
  5. 在不斷寫文件的過程中雁歌,通過廣播刷新下載進(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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市呆细,隨后出現(xiàn)的幾起案子型宝,更是在濱河造成了極大的恐慌,老刑警劉巖絮爷,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趴酣,死亡現(xiàn)場離奇詭異,居然都是意外死亡坑夯,警方通過查閱死者的電腦和手機(jī)岖寞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柜蜈,“玉大人仗谆,你說我怎么就攤上這事∈缏模” “怎么了隶垮?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長秘噪。 經(jīng)常有香客問我狸吞,道長,這世上最難降的妖魔是什么指煎? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任蹋偏,我火速辦了婚禮,結(jié)果婚禮上至壤,老公的妹妹穿的比我還像新娘威始。我一直安慰自己,他們只是感情好像街,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布字逗。 她就那樣靜靜地躺著,像睡著了一般宅广。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上些举,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天跟狱,我揣著相機(jī)與錄音,去河邊找鬼户魏。 笑死驶臊,一個胖子當(dāng)著我的面吹牛挪挤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播关翎,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼扛门,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纵寝?” 一聲冷哼從身側(cè)響起论寨,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎爽茴,沒想到半個月后葬凳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡室奏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年火焰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胧沫。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡昌简,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绒怨,到底是詐尸還是另有隱情纯赎,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布窖逗,位于F島的核電站址否,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏碎紊。R本人自食惡果不足惜佑附,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仗考。 院中可真熱鬧音同,春花似錦、人聲如沸秃嗜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锅锨。三九已至叽赊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間必搞,已是汗流浹背必指。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留恕洲,地道東北人塔橡。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓梅割,卻偏偏與公主長得像,于是被迫代替她去往敵國和親葛家。 傳聞我的和親對象是個殘疾皇子户辞,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容