Android 斷點(diǎn)續(xù)傳功能的實(shí)現(xiàn)

目錄

實(shí)現(xiàn)效果

按照慣例充坑,效果奉上


Kapture 2018-12-01 at 16.44.09.gif

前言

日更啊染突!日更捻爷!,上一篇比較劃水的給大家分享了一下份企,在做這個(gè)功能時(shí)候的一點(diǎn)Java基礎(chǔ)也榄,IO流,在這個(gè)過(guò)程中順便再講一下另一個(gè)IO流中的RandomAccessFile類司志,那么今天分享一下甜紫。

  1. OKHttp依賴:使用的是OKhttp來(lái)連接網(wǎng)絡(luò)
  2. RandomAccessFile 來(lái)進(jìn)行IO的讀寫
  3. BroadcastReceiver:用來(lái)接受和更新UI的

正文

OKManger

OKManger類中實(shí)現(xiàn)了 網(wǎng)絡(luò)連接方法,下載文件方法骂远,啟用線程方法囚霸;

public class OkManager {
    private File rootFile;//文件的路徑
    private File file;//文件
    private long downLoadSize;//下載文件的長(zhǎng)度
    private final ThreadPoolExecutor executor;// 線程池
    private boolean isDown = false; //是否已經(jīng)下載過(guò)了(下載后點(diǎn)擊暫停) 默認(rèn)為false
    private String name; //名稱
    private String path;// 下載的網(wǎng)址
    private RandomAccessFile raf; // 讀取寫入IO方法
    private long totalSize = 0;
    private MyThread thread;//線程
    private Handler handler;//Handler 方法
    private DownLoad.IProgress progress;// 下載進(jìn)度方法,內(nèi)部定義的抽象方法


    /**
     * 構(gòu)造方法  OKhttp
     * @param path  網(wǎng)絡(luò)連接路徑
     * @param progress  更新路徑
     */
    public OkManager(String path, DownLoad.IProgress progress) {
        this.path = path;
        this.progress = progress;
        this.handler = new Handler();
        this.name = path.substring(path.lastIndexOf("/") + 1);
        rootFile = FileUtils.getRootFile();
        executor = new ThreadPoolExecutor(5, 5, 50, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3000));
        //executor.execute(new MyThread());
          }

    /**
     *  自定義線程
     */
    class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            downLoadFile();
        }

    }

    /**
     * 這就是下載方法
     */
    private void downLoadFile() {
        try {
            if (file == null) {//判斷是否擁有相應(yīng)的文件
                file = new File(rootFile, name); //很正常的File() 方法
                raf = new RandomAccessFile(file, "rwd");//實(shí)例化一下我們的RandomAccessFile()方法
            } else {
                downLoadSize = file.length();// 文件的大小
                if (raf == null) {//判斷讀取是否為空
                    raf = new RandomAccessFile(file, "rwd");
                }
                raf.seek(downLoadSize);
            }
            totalSize = getContentLength(path);//獲取文件的大小
            if (downLoadSize == totalSize) {// 判斷是否下載完成
                //已經(jīng)下載完成
                return;
            }

            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder().url(path).
                    addHeader("Range", "bytes=" + downLoadSize + "-" + totalSize).build();
            Response response = client.newCall(request).execute();
            InputStream ins = response.body().byteStream();
            //上面的就是簡(jiǎn)單的OKHttp連接網(wǎng)絡(luò)激才,通過(guò)輸入流進(jìn)行寫入到本地
            int len = 0;
            byte[] by = new byte[1024];
            long endTime = System.currentTimeMillis();
            while ((len = ins.read(by)) != -1 && isDown) {//如果下載沒(méi)有出錯(cuò)并且已經(jīng)開(kāi)始下載拓型,循環(huán)進(jìn)行以下方法
                raf.write(by, 0, len);
                downLoadSize += len;
                if (System.currentTimeMillis() - endTime > 1000) {
                    final double dd = downLoadSize / (totalSize * 1.0);
                    DecimalFormat format = new DecimalFormat("#0.00");
                    String value = format.format((dd * 100)) + "%";//計(jì)算百分比
                    Log.i("tag", "==================" + value);
                    handler.post(new Runnable() {//通過(guò)Handler發(fā)送消息到UI線程,更新
                        @Override
                        public void run() {
                            progress.onProgress((int) (dd * 100));
                        }
                    });
                }
            }
            response.close();//最后要把response關(guān)閉
        } catch (Exception e) {
            e.getMessage();
        }

    }

    /**
     * 線程開(kāi)啟方法
     */
    public void start() {
        if (thread == null) {
            thread = new MyThread();
            isDown = true;
            executor.execute(thread);
        }
    }

    /**
     * 線程停止方法
     */
    public void stop() {
        if (thread != null) {
            isDown = false;
            executor.remove(thread);
            thread = null;
        }
    }

    //通過(guò)OkhttpClient獲取文件的大小
    public long getContentLength(String url) throws IOException {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(url).build();
        Response response = client.newCall(request).execute();
        long length = response.body().contentLength();
        response.close();
        return length;
    }

    public interface IProgress {
        void onProgress(int progress);
    }

}

以上的方法瘸恼,大家仔細(xì)的看一看劣挫,這里面有詳細(xì)的注釋,大家主要關(guān)注downLoadFile()方法东帅,

FileUtils
public class FileUtils {
    //判斷是否安裝SDCard
    public static boolean isSdOk(){
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
            return true;
        }
        return false;
    }
    //創(chuàng)建一個(gè)文件夾压固,用來(lái)存放下載的文件
    public static File getRootFile(){
        File sd = Environment.getExternalStorageDirectory();
        File rootFile = new File(sd,"TEMPFILE");
        if (!rootFile.exists()){
            rootFile.mkdirs();
        }
        return rootFile;
    }
}

這個(gè)就是文件寫入的路徑,封裝的方法靠闭,第一個(gè)就是判斷是否安裝SDCard 帐我,第二個(gè)方法創(chuàng)建文件夾,存放下載的文件阎毅;

activity_main.xml

布局文件焚刚,視圖,用了兩個(gè)按鈕和progressBar

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="us.mifeng.downloader.MainActivity">
    <ProgressBar
        android:layout_margin="10dp"
        android:layout_marginTop="20dp"
        android:id="@+id/progress"
        android:layout_width="match_parent"
        android:layout_height="5dp"
        style="@android:style/Widget.ProgressBar.Horizontal"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/progress"
        android:orientation="horizontal">

        <Button
            android:id="@+id/start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="開(kāi)始" />
        <Button
            android:id="@+id/stop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="停止"/>
    </LinearLayout>

</RelativeLayout>

主方法 Mainactivity()
public class MainActivity extends AppCompatActivity implements View.OnClickListener, DownLoad.IProgress {
    private String path = "https://download.alicdn.com/wireless/taobao4android/latest/702757.apk";
    //private DownLoad downLoad;
    private ProgressBar pBar;
    private OkManager downLoad;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //downLoad = new DownLoad(path,this);
        downLoad = new OkManager(path,this);
        initView();
    }

    private void initView() {
        Button start = (Button) findViewById(R.id.start);
        Button stop = (Button) findViewById(R.id.stop);
        pBar = (ProgressBar) findViewById(R.id.progress);
        pBar.setMax(100);
        start.setOnClickListener(this);
        stop.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.start:
                downLoad.start();
                break;
            case R.id.stop:
                downLoad.stop();
                break;
        }
    }
    //顯示進(jìn)度條
    @Override
    public void onProgress(int progress) {
        pBar.setProgress(progress);
    }
}

很簡(jiǎn)單就是調(diào)用了之前封裝的方法扇调,大家好好地看響應(yīng)的方法就可以了

總結(jié)

  1. 斷點(diǎn)續(xù)傳的關(guān)鍵是斷點(diǎn)矿咕,所以在制定傳輸協(xié)議的時(shí)候要設(shè)計(jì)好,如上圖,我自定義了一個(gè)交互協(xié)議碳柱,每次下載請(qǐng)求都會(huì)帶上下載的起始點(diǎn)捡絮,這樣就可以支持從斷點(diǎn)下載了, 其實(shí)HTTP里的斷點(diǎn)續(xù)傳也是這個(gè)原理莲镣,在HTTP的頭里有個(gè)可選的字段RANGE福稳,表示下載的范圍。
  2. Range : 用于客戶端到服務(wù)器端的請(qǐng)求瑞侮,可通過(guò)該字段指定下載文件的某一段大小的圆,及其單位。典型的格式如:
    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é)開(kāi)始到文件結(jié)束部分的內(nèi)容

感謝

這個(gè)代碼其實(shí)是我在Github上面找到的響應(yīng)的方法半火,比較簡(jiǎn)單越妈,所以可以更好的放入項(xiàng)目中,希望大家多去大神github上面star

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钮糖,一起剝皮案震驚了整個(gè)濱河市梅掠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌店归,老刑警劉巖阎抒,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異消痛,居然都是意外死亡且叁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門肄满,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谴古,“玉大人,你說(shuō)我怎么就攤上這事稠歉£#” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵怒炸,是天一觀的道長(zhǎng)带饱。 經(jīng)常有香客問(wèn)我,道長(zhǎng)阅羹,這世上最難降的妖魔是什么勺疼? 我笑而不...
    開(kāi)封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮捏鱼,結(jié)果婚禮上执庐,老公的妹妹穿的比我還像新娘。我一直安慰自己导梆,他們只是感情好轨淌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布迂烁。 她就那樣靜靜地躺著,像睡著了一般递鹉。 火紅的嫁衣襯著肌膚如雪盟步。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天躏结,我揣著相機(jī)與錄音却盘,去河邊找鬼。 笑死媳拴,一個(gè)胖子當(dāng)著我的面吹牛黄橘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播禀挫,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼旬陡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了语婴?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤驶睦,失蹤者是張志新(化名)和其女友劉穎砰左,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體场航,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缠导,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了溉痢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片僻造。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖孩饼,靈堂內(nèi)的尸體忽然破棺而出髓削,到底是詐尸還是另有隱情,我是刑警寧澤镀娶,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布立膛,位于F島的核電站,受9級(jí)特大地震影響梯码,放射性物質(zhì)發(fā)生泄漏宝泵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一轩娶、第九天 我趴在偏房一處隱蔽的房頂上張望儿奶。 院中可真熱鬧,春花似錦鳄抒、人聲如沸闯捎。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)隙券。三九已至男应,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間娱仔,已是汗流浹背沐飘。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留牲迫,地道東北人耐朴。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像盹憎,于是被迫代替她去往敵國(guó)和親筛峭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355