多线程断点续传

这里下载的斗鱼

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <EditText
        android:id="@+id/et_path"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入要下载的文件资源路径"
        android:text="http://dl001.liqucn.com/upload/2018/286/s/A_Sixrooms_xiuchang_9372-xiuchang_liqu_5.9.6_20180604-104643.apk" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="download"
        android:text="下载" />

    <ProgressBar
        android:id="@+id/pb0"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <ProgressBar
        android:id="@+id/pb1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <ProgressBar
        android:id="@+id/pb2"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>
public class MainActivity extends Activity {
    private EditText et_path;
    private ProgressBar pb0;
    private ProgressBar pb1;
    private ProgressBar pb2;
    /**
     * 开启几个线程从服务器下载数据
     */
    public static int threadCount = 3;

    public static int runningThreadCount;
    private String path;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化控件
        et_path = (EditText) findViewById(R.id.et_path);
        pb0 = (ProgressBar) findViewById(R.id.pb0);
        pb1 = (ProgressBar) findViewById(R.id.pb1);
        pb2 = (ProgressBar) findViewById(R.id.pb2);
    }

    //下载按钮的点击事件
    public void download(View view) {
        path = et_path.getText().toString().trim();
        if (TextUtils.isEmpty(path) || (!path.startsWith("http://"))) {
            Toast.makeText(this, "对不起路径不合法", 0).show();
            return;
        }
        new Thread(){
            public void run() {
                try {
                    //1.获取服务器上的目标文件的大小
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setConnectTimeout(5000);
                    conn.setRequestMethod("GET");
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        int length = conn.getContentLength();
                        System.out.println("服务器文件的长度为:" + length);
                        //2.在本地创建一个跟原始文件同样大小的文件
                        RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+getFileName(path), "rw");
                        raf.setLength(length);
                        raf.close();
                        //3.计算每个线程下载的起始位置和结束位置
                        int blocksize = length / threadCount;
                        runningThreadCount = threadCount;
                        for (int threadId = 0; threadId < threadCount; threadId++) {
                            int startIndex = threadId * blocksize;
                            int endIndex = (threadId + 1) * blocksize - 1;
                            if (threadId == (threadCount - 1)) {
                                endIndex = length - 1;
                            }
                            //4.开启多个子线程开始下载
                            new DownloadThread(threadId, startIndex, endIndex).start();
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }


    private class DownloadThread extends Thread {
        /**
         * 线程id
         */
        private int threadId;
        /**
         * 线程下载的理论开始位置
         */
        private int startIndex;
        /**
         * 线程下载的结束位置
         */
        private int endIndex;
        /**
         * 当前线程下载到文件的那一个位置了.
         */
        private int currentPosition;

        public DownloadThread(int threadId, int startIndex, int endIndex) {
            this.threadId = threadId;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            System.out.println(threadId + "号线程下载的范围为:" + startIndex
                    + "   ~~   " + endIndex);
            currentPosition = startIndex;
        }

        @Override
        public void run() {
            try {
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                //检查当前线程是否已经下载过一部分的数据了 
                File info = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position");
                RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+getFileName(path), "rw");
                if(info.exists()&&info.length()>0){
                    FileInputStream fis = new FileInputStream(info);
                    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                    currentPosition = Integer.valueOf(br.readLine());
                    conn.setRequestProperty("Range", "bytes="+currentPosition+"-"+endIndex);
                    System.out.println("原来有下载进度,从上一次终止的位置继续下载"+"bytes="+currentPosition+"-"+endIndex);
                    fis.close();
                    raf.seek(currentPosition);//每个线程写文件的开始位置都是不一样的.
                }else{
                //告诉服务器 只想下载资源的一部分
                    conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
                    System.out.println("原来没有有下载进度,新的下载"+ "bytes="+startIndex+"-"+endIndex);
                    raf.seek(startIndex);//每个线程写文件的开始位置都是不一样的.
                }
                InputStream is = conn.getInputStream();
                byte[] buffer = new byte[1024];
                int len = -1;
                while((len = is.read(buffer))!=-1){
                    //把每个线程下载的数据放在自己的空间里面.
//                    System.out.println("线程:"+threadId+"正在下载:"+new String(buffer));
                    raf.write(buffer,0, len);
                    //5.记录下载进度
                    currentPosition+=len;
                    File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position");
                    RandomAccessFile fos = new RandomAccessFile(file,"rwd");
                    //System.out.println("线程:"+threadId+"写到了"+currentPosition);
                    fos.write(String.valueOf(currentPosition).getBytes());
                    fos.close();//fileoutstream数据是不一定被写入到底层设备里面的,有可能是存储在缓存里面.
                    //raf 的 rwd模式,数据是立刻被存储到底层硬盘设备里面.

                    //更新进度条的显示
                    int max = endIndex - startIndex;
                    int progress = currentPosition - startIndex;
                    if(threadId==0){
                        pb0.setMax(max);
                        pb0.setProgress(progress);
                    }else if(threadId==1){
                        pb1.setMax(max);
                        pb1.setProgress(progress);
                    }else if(threadId==2){
                        pb2.setMax(max);
                        pb2.setProgress(progress);
                    }
                }
                raf.close();
                is.close();
                System.out.println("线程:"+threadId+"下载完毕了...");
                File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position");
                f.renameTo(new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position.finish"));
                synchronized (MainActivity.class) {
                    runningThreadCount--;
                    //6.删除临时文件
                    if(runningThreadCount<=0){
                        for(int i=0;i<threadCount;i++){
                            File ft = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+i+".position.finish");
                            ft.delete();
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 获取一个文件名称
     * @param path
     * @return
     */
    public String getFileName(String path){
        int start = path.lastIndexOf("/")+1;
        return path.substring(start);
    }
}

猜你喜欢

转载自blog.csdn.net/l6666_6666/article/details/80711921
今日推荐