美文网首页Android开发Android技术知识Android开发
android断点下载之多线程分段下载进阶

android断点下载之多线程分段下载进阶

作者: Ad大成 | 来源:发表于2019-08-13 19:44 被阅读4次

之前给大家分享了一下关于断点下载的基本套路和逻辑下面引入线程池来完成断点下载!主要就是让大家更深入的了解一下断点下载的各种样式!可以帮助新手对代码逻辑的一种锻炼!这次注解非常详细,我就不多说了!

下载的接口

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();
}

服务下载

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
            //先封装文件
        File directory = Environment.getExternalStorageDirectory();
        final File file = new File(directory, "1221.apk");
        //请求头信息 创建retrofit对象
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://www.wandoujia.com/")
                .build();
        //请求头信息 创建服务器对象 注解封装的接口对象
        RetrofitService service = retrofit.create(RetrofitService.class);
        //请求头信息 调用接口方法 获取call对象
        Call<Void> call = service.getHeaderMessage();
        //请求头信息 call执行异步任务 获取结果
        call.enqueue(new Callback<Void>() {
            @Override
            public void onResponse(Call<Void> call, Response<Void> response) {
                Log.i("tag", "onResponse: ");
                //响应对象获取请求的头信息对象
                Headers headers = response.headers();
                //利用请固定头字段 获取响应回来的文件大小
                String s = headers.get("content-length");
                //将文件大小转成long类型
                final long contentLength = Long.parseLong(s);
                //准备开启5个线程来下载该文件
                int threadCount=5;
                //计算每个线程需要下载的文件大小
                long partFile = contentLength / threadCount;
                //开启循环 开启线程池 下载文件
                for (int i = 0; i < threadCount; i++) {
                    /**
                     * 线程分配下载开始指针位置和结束指针位置
                     * range:bytes=start-end(断点续传的主要头信息必须配置的)
                     * partFile*i(start)-partFile*(i+1)-1(end)这里减一是分段下载的规律
                     * partFile*0(Start)-partFile*(0+1)-1(end)
                     * partFile*1(Start)-partFile*(1+1)-1(end)
                     * partFile*2(Start)-contentLength
                     *
                     */
                    //这里属于初始化每个线程开始和结束的时间点的位置 因为可能有断点的情况发生
                    //所以我们把时间断点在后面存在sp上 在这里取出sp里存储的时间断点位置
                    //如果有断点存进来那么就替换partstart如果没有就用原有的
                    //定义动态key
                    final String threadId="thread"+i;
                    //定义每段起始的start时间指针
                    long defaultStart = partFile * i;
                    //取出sp里存储的断点时间指针设为此段子线程的开始位置,默认值为defultStart
                    //也就是如果没有发生断点下载那就直接用默认值为起始断点时间指针即可
                     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;
                    }
                    //开始的时间指针和结束的时间指针安排完事就开始构建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", "当前线程是: "+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();
                                //创建RandomAccessFile
                                RandomAccessFile accessFile = new RandomAccessFile(file, "rw");
                                byte[] bytes = new byte[1024];
                                int len;
                                //将时间指针移动到开始位置开始读取
                                accessFile.seek(myspStart);
                                //创建sp
                                SharedPreferences mysp = getSharedPreferences("mysb", MODE_PRIVATE);
                                while ((len=inputStream.read(bytes))!=-1){
                                    //写入我们之前创建好的文件里
                                    accessFile.write(bytes,0,len);
                                    //这里就是sp的存档点了,如果出现事故断开连接,那么在这里就可以存入当前断开的
                                    //时间指针的位置
                                    //获取当前时间指针
                                    long filePointer = accessFile.getFilePointer();
                                    //存入sp内
                                    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("当前进度:" + 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);

        }
    }

总结:开启了多线程来分段式下载,提高了下载的速度,但是对于未来的5g来说也许就没有什么用了,但是往往更新更好用的工具和框架都是基于笨拙的老一代而生成的!

相关文章

网友评论

    本文标题:android断点下载之多线程分段下载进阶

    本文链接:https://www.haomeiwen.com/subject/khfijctx.html