android斷點下載之多線程分段下載進階

之前給大家分享了一下關(guān)于斷點下載的基本套路和邏輯下面引入線程池來完成斷點下載!主要就是讓大家更深入的了解一下斷點下載的各種樣式!可以幫助新手對代碼邏輯的一種鍛煉!這次注解非常詳細,我就不多說了!

下載的接口

public interface RetrofitService {

    @GET("https://www.wandoujia.com/apps/604363/download/dot?ch=detail_normal_dl")
    Call<ResponseBody> breadPointRetrofit(@Header("range") String range);

    @HEAD("https://www.wandoujia.com/apps/604363/download/dot?ch=detail_normal_dl")
    Call<Void> getHeaderMessage();
}

服務(wù)下載

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
            //先封裝文件
        File directory = Environment.getExternalStorageDirectory();
        final File file = new File(directory, "1221.apk");
        //請求頭信息 創(chuàng)建retrofit對象
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://www.wandoujia.com/")
                .build();
        //請求頭信息 創(chuàng)建服務(wù)器對象 注解封裝的接口對象
        RetrofitService service = retrofit.create(RetrofitService.class);
        //請求頭信息 調(diào)用接口方法 獲取call對象
        Call<Void> call = service.getHeaderMessage();
        //請求頭信息 call執(zhí)行異步任務(wù) 獲取結(jié)果
        call.enqueue(new Callback<Void>() {
            @Override
            public void onResponse(Call<Void> call, Response<Void> response) {
                Log.i("tag", "onResponse: ");
                //響應(yīng)對象獲取請求的頭信息對象
                Headers headers = response.headers();
                //利用請固定頭字段 獲取響應(yīng)回來的文件大小
                String s = headers.get("content-length");
                //將文件大小轉(zhuǎn)成long類型
                final long contentLength = Long.parseLong(s);
                //準(zhǔn)備開啟5個線程來下載該文件
                int threadCount=5;
                //計算每個線程需要下載的文件大小
                long partFile = contentLength / threadCount;
                //開啟循環(huán) 開啟線程池 下載文件
                for (int i = 0; i < threadCount; i++) {
                    /**
                     * 線程分配下載開始指針位置和結(jié)束指針位置
                     * range:bytes=start-end(斷點續(xù)傳的主要頭信息必須配置的)
                     * partFile*i(start)-partFile*(i+1)-1(end)這里減一是分段下載的規(guī)律
                     * partFile*0(Start)-partFile*(0+1)-1(end)
                     * partFile*1(Start)-partFile*(1+1)-1(end)
                     * partFile*2(Start)-contentLength
                     *
                     */
                    //這里屬于初始化每個線程開始和結(jié)束的時間點的位置 因為可能有斷點的情況發(fā)生
                    //所以我們把時間斷點在后面存在sp上 在這里取出sp里存儲的時間斷點位置
                    //如果有斷點存進來那么就替換partstart如果沒有就用原有的
                    //定義動態(tài)key
                    final String threadId="thread"+i;
                    //定義每段起始的start時間指針
                    long defaultStart = partFile * i;
                    //取出sp里存儲的斷點時間指針設(shè)為此段子線程的開始位置,默認值為defultStart
                    //也就是如果沒有發(fā)生斷點下載那就直接用默認值為起始斷點時間指針即可
                     SharedPreferences mysp = getSharedPreferences("mysp", MODE_PRIVATE);
                    final long myspStart = mysp.getLong(threadId, defaultStart);
                    //定義每段end時間指針
                    long end = partFile * (i + 1) - 1;
                    //如果是最后一圈也就是最后一段線程end為contentlength
                    if (i == threadCount - 1) {
                        end=contentLength;
                    }
                    //開始的時間指針和結(jié)束的時間指針安排完事就開始構(gòu)建Range頭
                    final String range=String.format(Locale.CHINESE,"bytes=%d-%d",myspStart,end);
                    //由于開啟多片下載 需要多個線程 所以線程池登場
                    Executors.newCachedThreadPool().execute(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                Log.i("tag", "當(dāng)前線程是: "+Thread.currentThread().getName());
                                //正式開始下載文件
                                Retrofit retrofit1 = new Retrofit.Builder()
                                        .baseUrl("https://www.wandoujia.com/")
                                        .build();
                                RetrofitService retrofitService = retrofit1.create(RetrofitService
                                        .class);
                                Call<ResponseBody> call1 = retrofitService.breadPointRetrofit(range);
                                Response<ResponseBody> execute = call1.execute();
                                //獲取下載流
                                InputStream inputStream = execute.body().byteStream();
                                //創(chuàng)建RandomAccessFile
//這里創(chuàng)建是為了每個線程操作各自的對象 如果在循環(huán)外創(chuàng)建會造成多線程操作同一個對象指針錯亂
                                RandomAccessFile accessFile = new RandomAccessFile(file, "rw");
                                byte[] bytes = new byte[1024];
                                int len;
                                //將時間指針移動到開始位置開始讀取
                                accessFile.seek(myspStart);
                                //創(chuàng)建sp
                                SharedPreferences mysp = getSharedPreferences("mysb", MODE_PRIVATE);
                                while ((len=inputStream.read(bytes))!=-1){
                                    //寫入我們之前創(chuàng)建好的文件里
                                    accessFile.write(bytes,0,len);
                                    //這里就是sp的存檔點了,如果出現(xiàn)事故斷開連接,那么在這里就可以存入當(dāng)前斷開的
                                    //時間指針的位置
                                    //獲取當(dāng)前時間指針
                                    long filePointer = accessFile.getFilePointer();
                                    //存入sp內(nèi)
                                    mysp.edit().putLong(threadId,filePointer).commit();
                                    long length = file.length();
                                    int currentProgress = (int) (length * 100 / contentLength);
                                    EventBus.getDefault().post(currentProgress);
                                }
                                EventBus.getDefault().post(file);

                            } catch (IOException e) {
                                e.printStackTrace();
                            }

                        }
                    });

                }



            }

            @Override
            public void onFailure(Call<Void> call, Throwable t) {

            }
        });

        return super.onStartCommand(intent, flags, startId);
    }

view層的代碼

  @Override
    public void onClick(View v) {
        startService(new Intent(this, MyService.class));
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void breadPoint(Integer index) {

        progressbar.setProgress(index);
        tv.setText("當(dāng)前進度:" + index);

    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void downloadApp(File files) {

        //        判斷小于7.0版本
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            Log.i("tag", "downloadApp: " + 99999);
            Intent intent = new Intent(Intent.ACTION_VIEW);
            //        7.0版本的uri方法
            Uri uri = Uri.fromFile(files);
            intent.setDataAndType(uri, "application/vnd.android.package-archive");
            startActivityForResult(intent, 2);
        } else {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            //        7.0以上版本用fileprovider
            Uri uri = FileProvider.getUriForFile(this, "com.example.breadpointdownload" + "" + "" +
                    ".provider", files);

            intent.setDataAndType(uri, "application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            startActivityForResult(intent, 2);

        }
    }

總結(jié):開啟了多線程來分段式下載,提高了下載的速度,但是對于未來的5g來說也許就沒有什么用了,但是往往更新更好用的工具和框架都是基于笨拙的老一代而生成的!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市摄乒,隨后出現(xiàn)的幾起案子蜈膨,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡草雕,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門固以,熙熙樓的掌柜王于貴愁眉苦臉地迎上來墩虹,“玉大人,你說我怎么就攤上這事憨琳〗氲觯” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵篙螟,是天一觀的道長菌湃。 經(jīng)常有香客問我,道長遍略,這世上最難降的妖魔是什么惧所? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮绪杏,結(jié)果婚禮上下愈,老公的妹妹穿的比我還像新娘。我一直安慰自己蕾久,他們只是感情好势似,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著腔彰,像睡著了一般叫编。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上霹抛,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音卷谈,去河邊找鬼杯拐。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的端逼。 我是一名探鬼主播朗兵,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼顶滩!你這毒婦竟也來了余掖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤礁鲁,失蹤者是張志新(化名)和其女友劉穎盐欺,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仅醇,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡冗美,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了析二。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粉洼。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖叶摄,靈堂內(nèi)的尸體忽然破棺而出属韧,到底是詐尸還是另有隱情,我是刑警寧澤蛤吓,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布宵喂,位于F島的核電站,受9級特大地震影響柱衔,放射性物質(zhì)發(fā)生泄漏樊破。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一唆铐、第九天 我趴在偏房一處隱蔽的房頂上張望哲戚。 院中可真熱鬧,春花似錦艾岂、人聲如沸顺少。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脆炎。三九已至,卻和暖如春氓辣,著一層夾襖步出監(jiān)牢的瞬間秒裕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工钞啸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留几蜻,地道東北人喇潘。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像梭稚,于是被迫代替她去往敵國和親颖低。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

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

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,089評論 1 32
  • 斷點下載由于4g比較慢 所以廣泛應(yīng)用,但是出了5g后估計就沒啥用了哈哈主要就是兩點第一:添加動態(tài)請求頭 Range...
    Ad大成閱讀 884評論 0 6
  • 一弧烤、簡歷準(zhǔn)備 1忱屑、個人技能 (1)自定義控件、UI設(shè)計暇昂、常用動畫特效 自定義控件 ①為什么要自定義控件莺戒? Andr...
    lucas777閱讀 5,186評論 2 54
  • 家有小兒任我行, 柔言軟語聽不清话浇。 但使三分顏色在脏毯, 染房開到北京城。
    沙平雁落下閱讀 418評論 5 8
  • 曾經(jīng)我心思單純幔崖,總是覺得一諾千金食店,人人皆善,坦誠相待赏寇,如今我爾虞我詐吉嫩,身被鎧甲,人鬼兩待嗅定,處處鋒芒自娩,只想保全自己。...
    坐擁王城閱讀 440評論 1 4