多线程下载 HttpURLConnection

Activity



/**实际开发涉及文件上传、下载都不会自己写这些代码,一般会使用第三方库(如xUtils)或Android提供的DownloadManager下载*/

public class HttpDownloadActivity extends ListActivity {

    private TextView tv_info;

    private LinearLayout ll_pbs;

    public static final String PATH_URL_SMALL = "http://f2.market.xiaomi.com/download/AppStore/0b6c25446ea80095219649f646b8d67361b431127/com.wqk.wqk.apk";

    public static final String PATH_URL_BIG = "http://f3.market.xiaomi.com/download/AppChannel/099d2b4f6006a4c883059f459e0025a3e1f25454e/com.pokercity.bydrqp.mi.apk";

    public static final String PATH_FILE = Environment.getExternalStorageDirectory().getPath() + File.separator + "bqt_download" + File.separator;

    /**下载完毕后安装下载的APK*/

    public static final int MSG_WHAT_DOWNLOAD_OK = 1;

    /**下载过程中更新信息*/

    public static final int MSG_WHAT_DOWNLOAD_INFO = 2;

    @SuppressLint("HandlerLeak")

    private Handler mHandler = new Handler() {

        @Override

        public void handleMessage(Message msg) {

            switch (msg.what) {

            case MSG_WHAT_DOWNLOAD_OK:

                Toast.makeText(HttpDownloadActivity.this, "下载完毕,请安装", Toast.LENGTH_SHORT).show();

                tv_info.append("\n路径为:" + (String) msg.obj);

                Intent intent = new Intent(Intent.ACTION_VIEW);

                intent.setDataAndType(Uri.parse("file://" + (String) msg.obj), "application/vnd.android.package-archive");

                startActivity(intent);

                break;

            case MSG_WHAT_DOWNLOAD_INFO:

                tv_info.append((String) msg.obj);

                break;

            }

        }

    };

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);

        String[] array = { "使用HttpURLConnection单线程下载文件", "使用HttpURLConnection多线程下载文件", "使用开源框架下载文件" };

        tv_info = new TextView(this);

        tv_info.setTextColor(Color.BLUE);

        tv_info.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);

        tv_info.setPadding(20, 10, 20, 10);

        getListView().addFooterView(tv_info);

        ll_pbs = new LinearLayout(this);

        ll_pbs.setOrientation(LinearLayout.VERTICAL);

        getListView().addFooterView(ll_pbs);

        setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<String>(Arrays.asList(array))));

        File directory = new File(PATH_FILE);

        if (!directory.exists()) directory.mkdirs();//必须有这一步

    }

    @Override

    protected void onListItemClick(ListView l, View v, int position, long id) {

        switch (position) {

        case 0://使用HttpURLConnection单线程下载文件

            tv_info.setText("下载过程信息:");

            new Thread() {

                @Override

                public void run() {

                    HttpDownloadFilesUtils.simpleDownLoad(PATH_URL_SMALL, PATH_FILE, false, mHandler);

                }

            }.start();

            break;

        case 1://使用HttpURLConnection多线程下载文件

            tv_info.setText("下载过程信息:");

            ll_pbs.removeAllViews();//清空掉旧的进度条

            final ArrayList<ProgressBar> pbs = new ArrayList<ProgressBar>();//ProgressBar、SeekBar、ProgressDialog 这些都是可以在子线程直接更新进度的

            for (int j = 0; j < HttpDownloadFilesUtils.THREAD_COUNT; j++) {

                ProgressBar progressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);

                ll_pbs.addView(progressBar);//添加到布局中

                pbs.add(progressBar);//添加到集合中

            }

            new Thread() {

                @Override

                public void run() {

                    HttpDownloadFilesUtils.mutileThreadDownload(PATH_URL_BIG, PATH_FILE, false, mHandler, pbs);

                }

            }.start();

            break;

        case 2:

            Toast.makeText(this, "请引用第三个库或jar包后自行测试", Toast.LENGTH_SHORT).show();

            break;

        }

    }

}

工具类


/** 下传文件工具类*/

public class HttpDownloadFilesUtils {

    /**直接使用URLConnection.openStream()打开网络输入流,然后将流写入到文件中*/

    public static void simpleDownLoad(String fileUrl, String filePath, boolean isUseUrlName, Handler mHandler) {

        String fileName;

        if (isUseUrlName) fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);//截取文件名及后缀名

        else fileName = new SimpleDateFormat("yyyy.MM.dd HH-mm-ss", Locale.CHINA).format(new Date()) + fileUrl.substring(fileUrl.lastIndexOf("."));

        fileName = fileName == null ? "bqt" : fileName;

        try {

            InputStream inputStream = new URL(fileUrl).openStream();

            OutputStream outputStream = new FileOutputStream(new File(filePath + fileName));

            byte[] buffer = new byte[1024];

            int len = 0;

            while ((len = inputStream.read(buffer)) > 0) {

                outputStream.write(buffer, 0, len);

            }

            inputStream.close();

            outputStream.close();

            mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_OK, filePath + fileName));

        } catch (MalformedURLException e) {

            e.printStackTrace();

        } catch (FileNotFoundException e) {

            e.printStackTrace();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    //*****************************************************************************************************************

    //                                                                                            多线程下载

    //*****************************************************************************************************************

    public static final int THREAD_COUNT = 3;// 线程的数量

    public static long blocksize;// 每个下载区块的大小

    public static int runningTHREAD_COUNT;// 正在运行的线程的数量

    /**

     * 多线程下载

     *  方式1:使用多线程分别下载文件的不同部分,最后把【合并】成一个文件(效率高)。方式2:使用java提供的【RandomAccessFile】类实现多线程的下载(简单)。

     * @param fileUrl        服务器路径

     * @param filePath    保存本地路径

     * @param isUseUrlName    是否使用服务器路径中的文件名,设为false则使用当前时间作为文件名

     * @param mHandler    通过mHandler和UI线程通讯

     * @param pbs        在子线程直接更新各线程下载进度,设为null则不考虑

     */

    public static void mutileThreadDownload(String fileUrl, String filePath, boolean isUseUrlName, Handler mHandler, ArrayList<ProgressBar> pbs) {

        try {

            HttpURLConnection conn = (HttpURLConnection) new URL(fileUrl).openConnection();//获取连接

            conn.setRequestMethod("GET");

            conn.setConnectTimeout(5000);

            if (conn.getResponseCode() == 200) {

                // 1、在本地创建一个大小跟服务器一模一样的空白文件

                long fileSize = conn.getContentLength();// 得到服务端文件的大小

                String info = "\n服务端文件的大小:" + fileSize + " ( " + fileSize / 1024 / 1024 + "M )";

                mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));

                String fileName;

                if (isUseUrlName) fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);//截取文件名及后缀名

                else fileName = new SimpleDateFormat("yyyy.MM.dd HH-mm-ss", Locale.CHINA).format(new Date()) + fileUrl.substring(fileUrl.lastIndexOf("."));

                fileName = fileName == null ? "bqt" : fileName;

                RandomAccessFile raf = new RandomAccessFile(filePath + fileName, "rw");//可以从指定位置开始读、写文件;模式:r、rw、rws、rwd

                raf.setLength(fileSize);//设定大小

                raf.close();

                // 2、开启若干个子线程分别去下载对应的资源

                blocksize = fileSize / THREAD_COUNT; // 每个下载区块的大小

                runningTHREAD_COUNT = THREAD_COUNT; //运行的线程数量

                for (int i = 1; i <= THREAD_COUNT; i++) {

                    long startIndex = (i - 1) * blocksize;//开始位置,从0开始

                    long endIndex = i * blocksize - 1;//结束位置

                    if (i == THREAD_COUNT) endIndex = fileSize - 1;// 最后一个线程的结束位置为 size - 1,若值比它大,也不会有异常,实际下载大小也是  size - 1

                    info = "\n开启线程 " + i + " ,下载范围:" + startIndex + "~" + endIndex;

                    mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));

                    //ProgressBar、SeekBar、ProgressDialog 这些都是可以在子线程直接更新进度的

                    if (pbs != null && pbs.get(i - 1) != null) pbs.get(i - 1).setMax((int) (endIndex - startIndex));//设置各个线程的进度条的最大值

                    //3、调用下面的逻辑完成多线程下载

                    new DownloadThread(fileUrl, filePath + fileName, i, startIndex, endIndex, mHandler, pbs).start();

                }

            }

            conn.disconnect();//取消连接

        } catch (MalformedURLException e) {

            e.printStackTrace();

        } catch (ProtocolException e) {

            e.printStackTrace();

        } catch (FileNotFoundException e) {

            e.printStackTrace();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    private static class DownloadThread extends Thread {//线程Thread的子类         private String fileUrl;         private String filePath;         private int threadId;         private long startIndex;         private long endIndex;         private Handler mHandler;         private ArrayList<ProgressBar> pbs;         /**定义一个记录当前线程已下载文件的大小的临时文件,若文件不存在则从头下载,否则从记录的位置继续下载;写入时则将其封装为RandomAccessFile*/         private File positionFile;         /***         * @param fileUrl        服务器路径         * @param filePath    缓存文件保存路径         * @param threadId        线程id,请使用0、1、2、3……形式,并请按顺序命名         * @param startIndex    当前线程开始下载的位置         * @param endIndex    当前线程结束下载的位置         * @param mHandler    通过mHandler和UI线程通讯         * @param pbs        在子线程直接更新各线程下载进度,设为null则不考虑         */         public DownloadThread(String fileUrl, String filePath, int threadId, long startIndex, long endIndex, Handler mHandler, ArrayList<ProgressBar> pbs) {             this.fileUrl = fileUrl;             this.filePath = filePath;             this.threadId = threadId;             this.startIndex = startIndex;             this.endIndex = endIndex;             this.mHandler = mHandler;             this.pbs = pbs;         }
        public void run() {             String info;             try {                 // 1、记录当前线程已下载的总大小                 int total = 0;// 初始值设为0,若已下载部分文件,则获取已下载部分文件的大小并重新赋值                 positionFile = new File(filePath + "-" + threadId);                 if (positionFile.exists() && positionFile.length() > 0) {                     BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(positionFile)));                     String totalstr = bufferedReader.readLine();// 获取当前线程上次下载的总【大小】是多少                     total = Integer.valueOf(totalstr);                     info = "\n上次线程" + threadId + "下载的总大小:" + total;                     mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));                     startIndex += total;//每个线程继续下载时的开始【位置】注意startIndex的值为开始下载的【位置】,是一个索引;total的值为实际下载的文件的【大小】                     bufferedReader.close();                 }
                //2、获取连接,设置连接的参数信息                 HttpURLConnection conn = (HttpURLConnection) new URL(fileUrl).openConnection();                 conn.setRequestMethod("GET");                 conn.setConnectTimeout(5000);                 conn.setRequestProperty("RANGE", "bytes=" + startIndex + "-" + endIndex);//指定每条线程从文件的什么位置开始下载,下载到什么位置为止                 // 注意,下载服务器中某一部分内容返回码是206,可以用【code/100==2】来判断                 InputStream inputStream = conn.getInputStream();//返回服务端返回的流
                //3、将流中的数据写入文件中                 RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "rw");                 randomAccessFile.seek(startIndex);// 指定文件开始写的位置(指针偏移量)。                 info = "\n第 " + threadId + " 个线程:写文件的开始位置:" + String.valueOf(startIndex);                 mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));                 int len = 0;                 byte[] buffer = new byte[1024];                 while ((len = inputStream.read(buffer)) != -1) {                     randomAccessFile.write(buffer, 0, len);//向randomAccessFile中写入读取到的流中的内容                     //将此文件封装成为一个RandomAccessFile,并采用采用rwd模式,即使断电也不会丢失信息!                     RandomAccessFile rf = new RandomAccessFile(positionFile, "rwd");                     total += len;//记录下载的总大小                     rf.write(String.valueOf(total).getBytes());                     rf.close();                     if (pbs != null && pbs.get(threadId - 1) != null) pbs.get(threadId - 1).setProgress(total);//设置当前进度条的进程值                 }                 inputStream.close();                 randomAccessFile.close();
            } catch (Exception e) {             } finally {                 //4、 所有的线程都下载完毕后删除记录文件                 synchronized (HttpDownloadFilesUtils.class) {//线程安全问题                     info = "\n线程 " + threadId + " 下载完毕了";                     mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));                     runningTHREAD_COUNT--;                     if (runningTHREAD_COUNT < 1) {                         info = "\n所有线程已下载完毕,删除临时文件";                         mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));                         for (int i = 1; i <= THREAD_COUNT; i++) {                             File temFile = new File(filePath + "-" + i);                             info = "\n删除临时文件 " + i + " ,状态: " + temFile.delete();//删除记录文件                             mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));                         }                         mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_OK, filePath));                     }                 }             }         }     } }

来自为知笔记(Wiz)

时间: 2024-08-27 22:55:06

多线程下载 HttpURLConnection的相关文章

使用HttpURLConnection多线程下载

1 import java.io.IOException; 2 import java.io.InputStream; 3 import java.io.RandomAccessFile; 4 import java.net.HttpURLConnection; 5 import java.net.MalformedURLException; 6 import java.net.ProtocolException; 7 import java.net.URL; 8 9 /** 10 * 线程类

Android 多线程下载,断点续传,线程池

你可以在这里看到这个demo的源码: https://github.com/onlynight/MultiThreadDownloader 效果图 这张效果图是同时开启三个下载任务,限制下载线程数量的效果图. 多线程下载原理 多线程下载的原理就是将下载任务分割成一个个小片段再将每个小片段分配给各个线程进行下载. 例如一个文件大小为100M,我们决定使用4个线程下载,那么每个线程下载的大小即为25M,每个线程的起始以及结束位置依次如下: 0: 0-25M 1: 25-50M 2: 50-75M 3

【幻化万千戏红尘】qianfengDay27-HttpURLConnection,OkHttpClient,,多线程下载且断点续传基础学习:

课程回顾: Servlet:java语言开发的运行在服务器上的开发步骤:1.创建Servlet类2.重写doGet或doPost方法3.运行在服务器 生命周期:1.初始化2.服务3.销毁 URL:统一资源定位符,网址openConnection 今日内容:Http协议:超文本传输协议常用方式:1.HttpURLConnection2.okHttp HttpURLConnection的使用步骤:1.创建URL对象---URL url=new URL("网址");2.获取连接对象--Htt

java多线程下载

多线程下载是通过占有服务器资源来提高速度的.每个线程应该下载一个文件的不同位置,假如现在有一个长度为10的资源,有三个线程,那么第一个线程下载的长度为012,第二个为345,第四个为6789所以问题来了每个线程下载的长度是多少呢?线程的id为0,1,2每个线程下载的数量=总长度/线程数量size=10/3start:id*sizeend:(id+1)*size-1最后一个线程的结束位置为:总长度-1 package down; import java.io.File; import java.i

J哥---------Android 多线程下载 仿下载助手(改进版)

首先声明一点: 这里的多线程下载 并不是指的 多个线程下载一个 文件,而是 每个线程 负责一个文件.真正的多线程 希望后面能给大家带来.  -------------  欢迎 爱学习的小伙伴 加群  -------------  -------------android交流群:230274309------------- -------------一起分享,一起进步!  需要你们-------------- --------------  期待各位爱学习的小伙伴们 的到来------------

Android之——多线程下载示例(一)

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46883927 一.概述 说到Android中的文件下载,Android API中明确要求将耗时的操作放到一个子线程中执行,文件的下载无疑是需要耗费时间的,所以要将文件的下载放到子线程中执行.下面,我们一起来实现一个Android中利用多线程下载文件的小例子. 二.服务端准备 在这个小例子中我以下载有道词典为例,在网上下载有道词典的安装包,在eclipse中新建项目web,将下载的

无废话Android之smartimageview使用、android多线程下载、显式意图激活另外一个activity,检查网络是否可用定位到网络的位置、隐式意图激活另外一个activity、隐式意图的配置,自定义隐式意图、在不同activity之间数据传递(5)

1.smartimageview使用 <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版多线程下载器核心代码分享

首先给大家分享多线程下载核心类: 1 package com.example.urltest; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.RandomAccessFile; 6 import java.net.HttpURLConnection; 7 import java.net.MalformedURLException; 8 import java.net.URL; 9 im

Android 之多线程下载原理

在Android之中呢,对于多线程的操作很是平凡,所以对于多线程的理解越深,那么对于自己的程序便能够很好的运行 这也是对于Android开发是一个重要的知识点,那么我们现在来了解多线程的下载原理. android 多线程下载 多线程下载步骤: 1.本地创建一个跟服务器一样的大小一样的文件 临时文件. 2.计算分配几个线程去下载服务器上的资源 每个文件下载的位置. 3.开启线程,每一个线程下载对应的文件. 4.如果所有的线程都把自己的数据下载完成了,服务器上的资源就被下载到本地了 如图所示:(假设