android中使用HttpURLConnection实现多线程下载

HttpURLConnection下载

HttpURLConnection下载步骤
1,将要下载的路径封装成一个url对象(一定是可以下载的路径)
2,通过url获取conn,并设置conn的各种属性,最重要的是setRequestMethod("GET");
3,判断code,如果是200,通过conn获取要下载的文件大小len
4,在本地创建一个RandomAccessFile文件,并设置长度 raf.setLength(len);
5,定义线程个数threadCount,每个线程要下载的字节大小threadSize,线程的开始下载位置startIndex,结束下载位置endIndex
6,for循环,给线程的threadId,线程的开始位置startIndex,endIndex进行赋值,并开启线程
7,定义一个MyThread类继承Thread,要放在主类的外面
8,定义threadId,startIndex,endIndex,并通过构造方法进行初始化
9,将要下载的路径封装成url对象
10,设置conn的属性,是重要的是设置请求头属性range ,conn.setRequestProperty("range" , "bytes="+startIndex+"-"+endIndex);
    如果range属性设置错误会出现416,作用,设置请求的文件的起始位置
11,获取响应码,如果是206,获取输入流
12,创建一个RandomAccessFile,并设置raf.seek(startIndex),表示从这个位置开始写出
13,将输入流is中的数据放入到内存,raf将内存中的数据写出到文件,并可以设置写出的位置
14,关闭输入流is和随机访问流raf

注意事项:
    1,要下载的路径一定是一个可下载的文件
    2,在知道要下载文件大小后,通过raf创建本地文件,一定要设置文件的大小为len
    3,在子线程中设置请求头range的时候一定要注意格式,conn.setRequestProperty("range","bytes:"+startIndex+"-"+endIndex);不然会出现416错误
    4,在创建raf对象后,一定要设置要写的开始位置 raf.seek(startIndex);
    5,下载的时候是请求方式是get,一定不能是Post不然会是411错误
HttpURLConnection断点续传下载步骤
1,在循环往外的写前,定义一个total用来记录从开始到现在写的字节个数
2,在循环里面,定义一个线程的当前位置int currentIndex = total + startIndex;
3,创建一个随机访问流,并设置模式为"rwd",直接写入硬盘
4,在设置请求头range前,创建一个bufferedReader,读取文件里面的数据,并将数据转换为int类型
5,然后将startIndex = 读取的结果

注意事项:
    要使用RandomAccessFile(file,"rwd")来写,不然写出的文件内容为0,这样读取的时候字符串转换为数据就为异常
HttpURLConnection断点续传完善
问题,每次下载完,.txt文件也写完了,如果不删除,下次下载的时候直接下载完成了
解决方案:
    1,在每个线程下载完之后,就将该.txt文件删除
    2,删除如果删不掉,因为没有写完之后,没有关raf的流

将HttpURLConnection断点续传 移值到android

动态的添加ProgerssBar
1,先创建一个布局main_activity.xml,里面添加一个LinearLayout,一定要加id
2,创建一个要动态添加的只包含ProgressBar的布局文件item_pro.xml,根标签一定是ProgressBar,sytle="?android"不是@android
3,点击之后,使用View填充一个ProgressBar,根据线程的个数将pb添加到ll_item中
4,每次点击之后要先清空ll_item中的子控件,ll_item.removeAllView();
将使用HttpURLConnection下载的代码移值过来
1,要添加2种权限,第一INTERNET,第二WRITE_EXTERNAL_STORAGE
2,将本来写出到当前项目下的文件,写出到sdcard,使用Environment.getExternalStorageDirectory()
3,因为网络请求要放到子线程中,所以将获取服务数据大小的并开启线程的代码放在download()方法中,然后将download放在子线程中
4,给ProgressBar赋值
    a,在MainActivity中创建一个HashMap<Integer,ProgressBar> hm;
    b,在创建ProgressBar的循环中,将pb添加到hm中
    c,在MyThread类的run方法中,给pb设置属性,max = endIndex - startIndex + 1;progress = currentIndex - startIndex +1;
    d,MyThread类要当作MainActivity的子类,不然在设置pb的时候会报异常,让使用Handler

效果

这里写图片描述

代码部分

package com.example.download3;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import com.example.download4.R;

public class MainActivity extends Activity implements OnClickListener {



    private HashMap<Integer, ProgressBar> hm = new HashMap<>();
    private EditText et_count;
    private Button bt_download;
    private LinearLayout ll_item;
    private int len;
    private int threadSize;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 初始化我们要使用的控件
        et_count = (EditText) findViewById(R.id.et_count);
        bt_download = (Button) findViewById(R.id.bt_download);
        ll_item = (LinearLayout) findViewById(R.id.ll_item);
        // 添加监听事件
        bt_download.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        // 添加控件之前,先清空控件
        ll_item.removeAllViews();

        /*
         * 点击之后,创建统一线程个数个progressbar
         */
        // 获取线程个数
        int count = Integer.parseInt(et_count.getText().toString().trim());
        for (int i = 0; i < count; i++) {
            ProgressBar pb = (ProgressBar) View.inflate(MainActivity.this,
                    R.layout.item_pro, null);
            hm.put(i, pb);
            // 再动态的将pb添加到线型布局中
            ll_item.addView(pb);

        }
        // download();
        new Thread() {
            public void run() {

                download();
            };
        }.start();

    }

    private void download() {
        // TODO Auto-generated method stub
        try {
            // 1,将请求的下载路径封装成一个url对象
            // URL url = new URL("http://localhost:8080/itheima74/feiq.exe");
            URL url = new URL("http://img1.3lian.com/2015/w2/60/d/41.jpg");
            // 获取httpurlconnection连接
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // 设置连接属性
            conn.setRequestMethod("GET");
            conn.setReadTimeout(5000);
            conn.setConnectTimeout(5000);
            int code = conn.getResponseCode();

            if (code == 200) {
                len = conn.getContentLength();
                // 在本地创建一个大小一样的RandomAccessFile文件,格式也要一样
                // RandomAccessFile raf = new RandomAccessFile(
                // new File("fuck.exe"), "rw");
                RandomAccessFile raf = new RandomAccessFile(new File(
                        Environment.getExternalStorageDirectory(), "pic.jpg"),
                        "rw");
                // 一定要文件的大小
                Log.e("图片大小", len + "");
                raf.setLength(len);
                Log.e("图片大小", 5555 + "");

                // 定义线程的个数threadCount,每个线程的要下载的大小 threadSize,线程的起始位置startIndex
                // 结束位置endIndex
                int threadCount = Integer.parseInt(et_count.getText()
                        .toString().trim());
                int startIndex;
                int endIndex;
                threadSize = len / threadCount;
                for (int threadId = 0; threadId < threadCount; threadId++) {
                    startIndex = threadId * threadSize;
                    endIndex = (threadId + 1) * threadSize - 1;
                    if (threadId == threadCount - 1) {
                        endIndex = len - 1;
                    }
                    System.out.println(threadId);
                    new MyThread(threadId, startIndex, endIndex).start();
                }
            }

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

    class MyThread extends Thread {
        int threadId;
        int startIndex;
        int endIndex;

        // 通过构造方法给线程的id,线程的开始位置,线程的结束位置进行初始化
        MyThread(int threadId, int startIndex, int endIndex) {
            this.threadId = threadId;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }

        @Override
        public void run() {
            // 将要下载的路径封装成一个URL对象
            ProgressBar pb = hm.get(threadId);
            try {
                // URL url = new
                // URL("http://localhost:8080/itheima74/feiq.exe");
                URL url = new URL("http://img1.3lian.com/2015/w2/60/d/41.jpg");
                // 通过url获取连接对象
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                // 设置连接对象的属性
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);
                File file = new File(Environment.getExternalStorageDirectory(),
                        threadId + ".txt");
                if (file.exists()) {
                    BufferedReader br = new BufferedReader(new FileReader(file));
                    startIndex = Integer.parseInt(br.readLine());
                    br.close();
                }
                System.out.println("线程" + threadId + "从" + startIndex + "开始下载");
                // 一定要记住要从某个位置到另一个位置下载,一定要设置请求头Range
                conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
                        + endIndex);
                int code = conn.getResponseCode();
                // 部分请求的响应码为206
                if (code == 206) {

                    // 获取响应的字节输入流
                    System.out.println("线程" + threadId + "开始下载");
                    InputStream is = conn.getInputStream();
                    // RandomAccessFile raf = new RandomAccessFile(new
                    // File("fuck.exe"), "rw");
                    RandomAccessFile raf = new RandomAccessFile(new File(
                            Environment.getExternalStorageDirectory(),
                            "pic.jpg"), "rw");
                    // 设置随机访问流开始写的位置
                    raf.seek(startIndex);
                    int len = -1; // 有效的字节码长度
                    byte[] buffer = new byte[5];// 定义一个小数组进行读入
                    int total = 0;

                    // 获取hashMap中的progressBar

                    while ((len = is.read(buffer)) != -1) {
                        raf.write(buffer, 0, len);

                        total = total + len;
                        int currentIndex = total + startIndex;
                        pb.setMax(endIndex - startIndex +1);
                        pb.setProgress(currentIndex - startIndex+1);

                        // 将当前读入的字节数写出到本地文件
                        RandomAccessFile raf1 = new RandomAccessFile(new File(
                                Environment.getExternalStorageDirectory(),
                                threadId + ".txt"), "rwd");
                        raf1.write((currentIndex + "").getBytes());
                        raf1.close();

                    }

                    // 关闭读入和字节流和写出的随机文件访问流
                    is.close();
                    raf.close();

                    System.out.println("线程" + threadId + "下载结束");
                    File file1 = new File(
                            Environment.getExternalStorageDirectory(), threadId
                                    + ".txt");
                    file1.delete();

                }

            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值