第11天-安卓多线程下载

今天来记录一下安卓的多线程下载。

先来说一下整体的一个思路:因为是要去下载目标文件,那么我们就需要先使用目标下载url来获得目标文件的大小,以及文件名(以便在本地创建该文件名)。然后开始创建线程,创建线程使用for循环,相信大家都懂。对于创建的线程,我们可以写具体的方法,来实现相应的下载任务。

首先创建一个emptyAvtivity。
由于初学者对多线程机制不了解,所以不推荐直接在项目中写代码。在File中New ModuleNew Module具体实现方法
在弹出对话框选择,JAVA Library,看图。
JAVA Library
点击Next,为我们的项目重命名一下,命名为MultiDownloader命名为MultiDowonLoader
点击Finish完成创建。此时创建的类是在LIb目录下,具体看图创建的JAVA类
要完成下载,需要新建一个main方法。具体代码如下。。。

package com.glsite.lib;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class MultiDownloader {
    /**
     * 总共的线程数
     */
    public static final int TOTAL_THREAD_COUNT = 3;
    /**
     * 要下载的连接
     */
    public static String path = "http://localhost:8080/Day10/qq.exe";
    /**
     * 运行状态下的线程数
     */
    private static int runningThreadCount = 0;

    public static void main(String[] args) {
        try {
            URL url = new URL(path);
           HttpURLConnection coon = (HttpURLConnection) url.openConnection();
           //请求方式是GET
            coon.setRequestMethod("GET");
            //获取响应码
            int code = coon.getResponseCode();
            //对响应码进行判断
            if (code == 200) {
                //如果响应码是200,那么返回目标的长度(大小)
                int length = coon.getContentLength();
                System.out.println("file length:" + length);
                //对下载的文件进行命名,命名的规则写了一个getDownloadFileName方法
                RandomAccessFile raf = new RandomAccessFile(getDownloadFileName(path), "rw");
                //我们在本地新建一个和目标文件大小一样的文件,用过迅雷的都知道吧
                raf.setLength(length);
                raf.close();
                //设置每个线程下载的大小,其中TOTAL_THREAD_COUNT表示分成的线程数
                int blockSize = length / TOTAL_THREAD_COUNT;
                System.out.println("every block size:" + blockSize);
                
                runningThreadCount = TOTAL_THREAD_COUNT;
                //开始创建线程

                for (int threadID = 0; threadID < TOTAL_THREAD_COUNT; threadID++) {
                    int startPosition = threadID * blockSize;
                    //下面公式最后-1是因为,blocksize是从1开始,而文件下载时从0开始,所以要减一
                    int endPosition = (threadID + 1) * blockSize - 1;
                    if (threadID == (TOTAL_THREAD_COUNT - 1)) {
                        endPosition = length - 1 ;
                    }



                    //threadID, startPosition, endPosition分别表示线程的id,该线程的起始位置,该线程的结束位置
                    new DownloadThread(threadID, startPosition, endPosition).start();
                }




            } else {
               System.out.println("download error, code = " + code);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 从网络下载路径获取文件名
     * @param path
     * 网络路径
     * @return 文件名
     */

    private static String getDownloadFileName(String path) {
        //获取最后一个/后的文件进行命名
        return path.substring(path.lastIndexOf("/") + 1);
    }

    private static class DownloadThread extends Thread{
        /**
         * 当前线程ID
         */
        private  int threadID;
        /**
         *当前线程起始位置
         */
        private  int startPosition;
        /**
         *当前线程结束位置
         */
        private  int endPosition;
       

        public DownloadThread(int threadID, int startPosition, int endPosition) {
            this.threadID = threadID;
            this.startPosition = startPosition;
            this.endPosition = endPosition;

        }

        @Override
        public void run() {
            System.out.println("thread:" + threadID + "beginworking");

            try {
                File finfo = new File(TOTAL_THREAD_COUNT + getDownloadFileName(path) + threadID + ".txt");
                if (finfo.exists() && finfo.length() > 0) {
                    FileInputStream fis = new FileInputStream(finfo);
                    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                    String lastposition = br.readLine();
                    startPosition = Integer.parseInt(lastposition);
                    fis.close();
                }


                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                System.out.println("begin and end:" + threadID + "range of download:" + startPosition + "~~" + endPosition);
                //下面的s1中的-表示谁到谁的意思
                conn.setRequestProperty("Range","bytes=" + startPosition + "-" + endPosition);
                int code = conn.getResponseCode();
                //这里的code是206,不要有疑问,因为是分段下载,所以是206,哈哈哈,不过我暂时也不知道为啥,可能是规则吧。
                if (code == 206) {
                    //如果响应码是206,那么就要开始下载了,传入一个输入流
                    InputStream is = conn.getInputStream();
                    RandomAccessFile raf = new RandomAccessFile(getDownloadFileName(path), "rw");
                    //从起始位置开始
                    raf.seek(startPosition);
                    //将流输入写入
                    int len = 0;
                    // 这个buffer的byte一般写1024,至于为啥,我也不知道
                    byte[] buffer = new byte[1024];
                    int total = 0;//downloaded data of current thread in this time
                    while ((len = is.read(buffer))!= -1) {
                        raf.write(buffer,0,len);
                        total += len;
                        RandomAccessFile inforraf = new RandomAccessFile(TOTAL_THREAD_COUNT + getDownloadFileName(path) + threadID + ".txt", "rwd");
                        inforraf.write(String.valueOf(startPosition + total).getBytes());
                        inforraf.close();
                    }
                    is.close();
                    raf.close();
                    System.out.println("thread:" + threadID + "download complete...");



                } else {
                   System.out.println("request download failed" );
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                synchronized (MultiDownloader.class) {
                    runningThreadCount--;
                    if (runningThreadCount <= 0) {
                        System.out.println("multi thread download complete.");
                        for (int i = 0; i < TOTAL_THREAD_COUNT; i++) {
                            File finfo = new File(TOTAL_THREAD_COUNT + getDownloadFileName(path) + i + ".txt");
                            System.out.println(finfo.delete());
                        }
                    }
                }
            }

        }
    }
}

以上就是JAVA的多线程下载了,支持断点续传奥。下面我们就开始将它移植到我们的安卓app上面。

-------------------------我是华丽的分割线-----------------------------------------------------------------

先上一张,总体布局图
大体布局图
将mainactivity的代码改成下面这样就完事了

package com.glsite.multidownloader;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText mEtPath;
    private EditText mEtThreadCount;
    private LinearLayout mLLContainer;
    private Button mBtSelf;
    private Button mBtOther;

    /**
     * 总共的线程数
     */
    public int totalThreadCount = 3;

    /**
     * 要下载的文件的链接
     */
    public String path = "http://192.168.1.130:8080/Day10/QQ.exe";

    /**
     * 运行状态的线程数
     */
    private static int runningThreadCount = 0;

    /**
     * ProgressBar的集合
     */
    private ArrayList<ProgressBar> mPbs;

    /**
     * 当前app的缓存目录
     */
    private String CACHE_DIR;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 1.找到控件
        // 2.在button点击事件当中下载文件
        // 3.下载的时候要在界面显示下载的进度
        mEtPath = findViewById(R.id.et_path);
        mEtThreadCount = findViewById(R.id.et_threadcount);
        mLLContainer = findViewById(R.id.ll_container);
        mBtSelf = findViewById(R.id.bt_self);
        mBtOther = findViewById(R.id.bt_other);

        mBtSelf.setOnClickListener(this);
        mBtOther.setOnClickListener(this);

        // 初始化缓存目录路径
        CACHE_DIR = this.getCacheDir().getAbsolutePath() + "/";
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_self:
                downloadBySelf();
                break;
            case R.id.bt_other:

                break;
        }
    }

    /**
     * 通过自己自定义的多线程代码去下载网络文件
     */
    private void downloadBySelf() {
        path =  mEtPath.getText().toString().trim();
        totalThreadCount = Integer.valueOf(mEtThreadCount.getText().toString().trim());
        mLLContainer.removeAllViews();
        mPbs = new ArrayList<>();
        for (int i = 0; i < totalThreadCount; i++) {
            // 有几个线程就添加几个progressbar
            ProgressBar pb = (ProgressBar) View.inflate(this, R.layout.pb, null);
            mLLContainer.addView(pb);
            mPbs.add(pb);
        }

        new Thread() {
            @Override
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        int length = conn.getContentLength();
                        System.out.println("file length:" + length);
                        RandomAccessFile raf = new RandomAccessFile(CACHE_DIR + getDownloadFileName(path), "rw");
                        // 创建一个空的文件并且设置它的文件长度等于服务器上的文件长度
                        raf.setLength(length);
                        raf.close();

                        int blockSize = length / totalThreadCount;
                        System.out.println("every block size:" + blockSize);

                        runningThreadCount = totalThreadCount;

                        for (int threadId = 0; threadId < totalThreadCount; threadId++) {
                            int startPosition = threadId * blockSize;
                            int endPosition = (threadId + 1) * blockSize -1;
                            if (threadId == (totalThreadCount - 1 )) {
                                endPosition = length - 1;
                            }
                            new DownloadThread(threadId, startPosition, endPosition).start();
                        }

                    } else {
                        System.out.println("download error, code = " + code);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();

    }

    /**
     * 从网络路径获取文件名
     *
     * @param path
     *          网络路径
     * @return  文件名
     */
    private static String getDownloadFileName(String path) {
        return path.substring(path.lastIndexOf("/") + 1);
    }

    /**
     * 下载文件的线程
     */
    private class DownloadThread extends Thread{
        /**
         * 线程id
         */
        private int threadId;

        /**
         * 当前现成下载的起始位置
         */
        private int startPosition;

        /**
         * 当前线程下载的终止位置
         */
        private int endPosition;

        /**
         * 当前线程需要去下载的总共的字节
         */
        private int threadTotal;

        /**
         * 该线程上一次下载了的字节数
         */
        private int lastDownloadTotalSize;

        public DownloadThread(int threadId, int startPosition, int endPosition) {
            this.threadId = threadId;
            this.startPosition = startPosition;
            this.endPosition = endPosition;
            this.threadTotal = endPosition - startPosition;
            mPbs.get(threadId).setMax(threadTotal);
        }

        @Override
        public void run() {

            System.out.println("thread:" + threadId + "begin working");

            try {

                File finfo = new File(CACHE_DIR + totalThreadCount + getDownloadFileName(path) + threadId + ".txt");
                if (finfo.exists() && finfo.length() > 0) {
                    FileInputStream fis = new FileInputStream(finfo);
                    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                    String lastPosition = br.readLine();
                    // 这里计算出来的就是表示的上次该线程下载了多少个字节总数
                    lastDownloadTotalSize = Integer.parseInt(lastPosition) - startPosition;
                    startPosition = Integer.parseInt(lastPosition);
                    fis.close();
                }

                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                System.out.println("begin and end:" + threadId + " range of download: " + startPosition + "~~" + endPosition);
                conn.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
                int code = conn.getResponseCode();
                if (code == 206) {
                    InputStream is = conn.getInputStream();
                    RandomAccessFile raf = new RandomAccessFile(CACHE_DIR + getDownloadFileName(path), "rw");

                    raf.seek(startPosition);

                    int len = 0;
                    byte[] buffer = new byte[1024];
                    int total = 0; // downloaded data of current thread in this times;
                    while ((len = is.read(buffer)) != -1) {
                        raf.write(buffer, 0, len);

                        total += len;
                        RandomAccessFile inforaf = new RandomAccessFile(CACHE_DIR + totalThreadCount + getDownloadFileName(path) + threadId + ".txt", "rwd");
                        inforaf.write(String.valueOf(startPosition + total).getBytes());
                        inforaf.close();
                        mPbs.get(threadId).setProgress(total + lastDownloadTotalSize);
                    }

                    is.close();
                    raf.close();
                    System.out.println("thread:" + threadId + " download complete...");
                } else {
                    System.out.println("request download failed.");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                synchronized (MainActivity.class) {
                    runningThreadCount--;
                    if (runningThreadCount <= 0) {
                        System.out.println("multi thread download complete.");
                        for (int i = 0; i < totalThreadCount; i++) {
                            File finfo = new File(CACHE_DIR + totalThreadCount + getDownloadFileName(path) + i + ".txt");
                            // System.out.println(finfo.delete());
                        }
                    }
                }
            }
        }
    }
}

好了,这样就完成啦,嘿嘿。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值