多媒体编程(多线程下载、断点续传、可以暂停、继续)

背景

  • 我觉得现在出生的码农是幸福的,为什么这么说呢,因为android2.0时代是一去不复返,那时候网上没有第三方的框架,网上没有抄袭,everything都是靠自己写,靠自己对着长长的源码瞅原理,对于眼睛小的人,瞬间都是1000000点伤害啊,想想都有吐血的感觉,但是现在有很多第三方的东西,用起来很也爽,所以我要偷偷的告诉大家,有时候庆幸我可以比老程序员多撩几年妹,哈哈哈。。那小伙子们在爽的同时,是不是也该了解其原理呢,接下来带大家看看《多线程下载》原理!!!!

效果图先献给各位再讨论说话![这里写图片描述]

这里写图片描述

步骤

  • (1)确定线程线程
//设置线程的数量
 public int threadCount = 3;`
  • (2)通过链接访问服务器,通过链接获取到文件的总大小,然后平分给3个线程小弟(注意:有时候不能平分,就让最后那个小弟多挡当一点) 如下图:

这里写图片描述

看完图了之后,我相信懂了大致原理,我现在上代码:

//防止主线程堵塞,我们一般创建子线程处理:
       //恢复下载
        isLoad = true;
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // ①获取图片的总大小,计算每个线程应承当的大小
                    URL url = new URL(filePath);
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setConnectTimeout(5000);
                    connection.setReadTimeout(5000);

                    Log.i("TAG", "响应码::" + connection.getResponseCode());
                    if (connection.getResponseCode() == 200) {
                        //得到文件大小
                        allLength = connection.getContentLength();

                        progressBar.setMax(allLength);
                        //②生成临时文件,并设置临时文件的大小
                        //这里为什么要用RandomAccessFile,而不是FileOutput,原因是RandomAccessFile有个
                        //Random单词,随机的意思,就意味着我们更加的灵活的读写,详细我就不多啰嗦了,在网上可以查看
                        File file = new File(Environment.getExternalStorageDirectory(), fileName);
                        RandomAccessFile raf = new RandomAccessFile(file, "rwd");
                        raf.setLength(allLength);
                        raf.close();

                        //现在就给线程小弟分任务了
                        int averAge = allLength / threadCount;
                        for (int i = 0; i < threadCount; i++) {
                            startPostion = averAge * i;
                            if (i == threadCount - 1) {
                                endPositon = allLength - 1;
                            } else {
                                endPositon = (i + 1) * averAge - 1;
                            }
                            new MyThread(startPostion, endPositon, i).start();

                        }

                    }

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


            }
        }).start();
  • (3)每个子线程设置好下载区间,再一次网络请求,并且要把每一次暂停之后,每个线程下载了多少,要记录下来,在一次点击下载时,并获取出来。
                 //获取暂停每个子线程下载的大小
                File file = new File(Environment.getExternalStorageDirectory(), threadId + ".text");
                //获取暂停以后,每个线程下载的大小
                if (file.exists()) {
                    FileInputStream inputStream = new FileInputStream(file);
                    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                    int newStart = Integer.parseInt(reader.readLine());
                    startPostion += newStart;
                }
                Log.i(TAG, "线程" + threadId + "小弟开始下载位置是" + startPostion);
                Log.i(TAG, "线程" + threadId + "小弟结束下载位置是" + endPostion);
                URL url = new URL(filePath);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setReadTimeout(5000);
                connection.setConnectTimeout(5000);
                //设置本次http请求所请求的数据的区间
                connection.setRequestProperty("Range", "bytes=" + startPostion + "-" + endPostion);
                if (connection.getResponseCode() == 206) {
                    InputStream inputStream = connection.getInputStream();
                    Log.i(TAG, "线程" + threadId + "要下载的总大小::" + connection.getContentLength());
                    File f = new File(Environment.getExternalStorageDirectory(), fileName);
                    RandomAccessFile randomAccessFile = new RandomAccessFile(f, "rwd");
                    randomAccessFile.seek(startPostion);

                    byte[] b = new byte[1024];
                    int len = 0;
                    //记录每个线程下载的大小
                    int total = 0;
                    while ((len = inputStream.read(b)) != -1) {
                        randomAccessFile.write(b, 0, len);
                        total += len;
                        Log.i(TAG, "线程小弟" + threadId + "已经帮你下载了::" + total);

                        allCount += len;
                        progressBar.setProgress(allCount);
                        int processCount = progressBar.getProgress()* 100/progressBar.getMax()*100;
                        Log.e(TAG,"processCount::"+processCount);
                        //每两个点更新一次 进度条
                        if (processCount%2==0){
                            handler.sendEmptyMessage(0);
                        }

                        Log.i(TAG, "所有线程下载总量::" + allCount);
                        //不要好奇为什么可以在子线程里面更新UI,那是因为android把progprogress设计就计算加载进度的
                        RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
                        accessFile.write((total + "").getBytes());
                        accessFile.close();
                        if(!isLoad){
                            return;
                        }
                    }
                    Log.i(TAG, "线程" + threadId + "已经下载完毕::总大小为:::" + total);
                    randomAccessFile.close();
                    loadCount++;
                    synchronized (filePath) {
                        if (loadCount == threadCount) {
                            allCount = 0;
                            for (int i = 0; i < threadCount; i++) {
                                File file1 = new File(Environment.getExternalStorageDirectory(), i + ".text");
                                if (file1.exists()) {
                                    file1.delete();
                                }
                                loadCount = 0;
                            }
                        }
                    }
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
  • (4) 当然了,我们的需求不紧紧只能下载,有时候手贱不小心点击错了,我们得暂停,所以在这里设置一个变量: boolean isLoad ;
  • 暂定代码:
/**
     * 停止下载
     * @param view
     */
    public void stopLoad(View view){
        isLoad = false;
    }
    //这里检测到标记就会结束
    if(!isLoad){
            return;
       }

最后祝福几句

  • 这里只是告诉大家下载的原理,在实际开发中,我们是要经过调接口的,接口会直接返回文件大小,名称等,所以我们得把这些数据用HashMap,存储起来,记录下载的大小,和暂定时记录已经下载量,还有我这里是直接写在Activity中,建议大家把代码封装到Service里面。
    随便把xml布局献上:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    android:gravity="center"
    android:orientation="vertical">


    <Button
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:lines="1"
        android:onClick="startLoad"
        android:text="开始下载"
        android:textSize="23sp" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:layout_marginTop="30dp"
        android:lines="1"
        android:onClick="stopLoad"
        android:text="暂停下载"
        android:textSize="23sp" />

    <ProgressBar
        android:id="@+id/pb_load_count"
        style="@android:style/Widget.ProgressBar.Horizontal"
        android:layout_width="match_parent"
        android:layout_height="10dp"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_marginTop="15dp"
        android:max="100"
        android:progress="0"
        android:progressDrawable="@drawable/processbackg"
        android:visibility="visible" />

    <TextView
        android:id="@+id/tv_load_count"
        android:layout_marginTop="15dp"
        android:textSize="26sp"
        android:textColor="#ff0000"
        android:text="0%"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

//变量名称


 private String TAG = "MainActivitya";

    //下载路径  我直接找了一个地址,写死了
    private static String filePath = "http://gdown.baidu.com/data/wisegame/91319a5a1dfae322/baidu_16785426.apk";

    //文件名
    private String fileName = "baidu_16785426.apk";

    //设置线程的数量
    public int threadCount = 3;

    //开始位置
    public int startPostion;

    //结束位置
    public int endPositon;

    //记录所有线程整个文件大小
    public int allCount;

    //记录下载完线程的数量
    public int loadCount = 0;

    //是否继续下载
    public boolean isLoad = true;

    //下载精度条
    public ProgressBar progressBar;

    //下载进度
    public TextView tvProcess;

    //总长度
    public int allLength;

谢谢大家欣赏!!!别忘了给觉老湿点个赞,鼓励鼓励。。哈哈

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值