使用HttpURLConnection实现多线程下载

HttpURLConnection继承了URLConnection,因此也可用于向指定网站发送GET请求、POST请求,而且它在URLConnection基础上提供了如下便捷方法:

实现多线程下载的步骤:

下面用一个示例来示范使用HttpURLConnection实现多线程下载。此代码来源疯狂讲义一书,该代码主要思路:在Activity中点击按钮,调用DownUtil的download()方法,在download()中启动四个线程去下载资源,每个线程负责下载自己的那部分资源,代码如下:

Activity:

[java][/java] view plaincopy

  1. package com.home.activity;
  2. import java.util.Timer;
  3. import java.util.TimerTask;
  4. import android.app.Activity;
  5. import android.os.Bundle;
  6. import android.os.Handler;
  7. import android.os.Message;
  8. import android.view.View;
  9. import android.view.View.OnClickListener;
  10. import android.widget.Button;
  11. import android.widget.EditText;
  12. import android.widget.ProgressBar;
  13. import com.home.multithreaddown.R;
  14. import com.home.util.DownUtil;
  15. public class MultiThreadDownActivity extends Activity {
  16.     private EditText urlText;
  17.     private EditText targetText;
  18.     private Button downBtn;
  19.     private ProgressBar bar;
  20.     private DownUtil downUtil;
  21.     private int mDownStatus;
  22.     private Handler handler;
  23.     @Override
  24.     protected void onCreate(Bundle savedInstanceState) {
  25.         super.onCreate(savedInstanceState);
  26.         setContentView(R.layout.main);
  27.         // 获取界面中控件
  28.         targetText = (EditText) findViewById(R.id.main_et_name);
  29.         urlText = (EditText) findViewById(R.id.main_et_url);
  30.         downBtn = (Button) findViewById(R.id.main_btn_download);
  31.         bar = (ProgressBar) findViewById(R.id.main_progressBar);
  32.         // 创建一个Handler对象
  33.         handler = new Handler() {
  34.             public void handleMessage(Message msg) {
  35.                 if (msg.what == 0x123) {
  36.                     bar.setProgress(mDownStatus);
  37.                 }
  38.             }
  39.         };
  40.         downBtn.setOnClickListener(new OnClickListener() {
  41.             @Override
  42.             public void onClick(View v) {
  43.                 // 初始化DownUtil对象
  44.                 downUtil = new DownUtil(urlText.getText().toString(),
  45.                         targetText.getText().toString(), 4);
  46.                 try {
  47.                     // 开始下载
  48.                     downUtil.download();
  49.                 } catch (Exception e) {
  50.                     e.printStackTrace();
  51.                 }
  52.                 // 定义每秒调度获取一次系统的完成进度
  53.                 final Timer timer = new Timer();
  54.                 timer.schedule(new TimerTask() {
  55.                     public void run() {
  56.                         // 获取下载任务的完成比率
  57.                         double completeRate = downUtil.getCompleteRate();
  58.                         mDownStatus = (int) (completeRate * 100);
  59.                         // 发送消息通知界面更新进度条
  60.                         handler.sendEmptyMessage(0x123);
  61.                         // 下载完成后取消任务调度
  62.                         if (mDownStatus >= 100) {
  63.                             timer.cancel();
  64.                         }
  65.                     }
  66.                 }, 0, 100);
  67.             }
  68.         });
  69.     }
  70. }

下载的工具类(DownUtil):

[java][/java] view plaincopy

  1. package com.home.util;
  2. import java.io.InputStream;
  3. import java.io.RandomAccessFile;
  4. import java.net.HttpURLConnection;
  5. import java.net.URL;
  6. public class DownUtil {
  7.     // 定义下载资源的路径
  8.     private String path;
  9.     // 指定所下载的文件的保存位置
  10.     private String targetFile;
  11.     // 定义需要使用多少线程下载资源
  12.     private int threadNum;
  13.     // 定义下载的文件的总大小
  14.     private int fileSize;
  15.     // 定义下载的线程对象
  16.     private DownloadThread[] threads;
  17.     public DownUtil(String path, String targetFile, int threadNum) {
  18.         this.path = path;
  19.         this.threadNum = threadNum;
  20.         // 初始化threads数组
  21.         threads = new DownloadThread[threadNum];
  22.         this.targetFile = targetFile;
  23.     }
  24.     public void download() throws Exception {
  25.         URL url = new URL(path);
  26.         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  27.         conn.setConnectTimeout(5 * 1000);
  28.         conn.setRequestMethod(“GET”);
  29.         conn.setRequestProperty(
  30.                 “Accept”,
  31.                 “image/gif,image/jpeg,image/pjpeg,application/x-shockwaveflash,application/x-ms-xbap,application/xaml+xml,application/vnd.ms-xpsdocument,application/x-ms-application,application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword,*/*”);
  32.         conn.setRequestProperty(“Accept-Language”, “zh-CN”);
  33.         conn.setRequestProperty(“Charset”, “UTF-8”);
  34.         conn.setRequestProperty(
  35.                 “User-Agent”,
  36.                 “Mozilla/4.0(compatible;MSIE7.0;Windows NT 5.2;Trident/4.0;.NET CLR 1.1.4322;.NET CLR 2.0.50727;.NET CLR 3.0.04506.30;.NET CLR 3.0.4506.2152;.NET CLR 3.5.30729)”);
  37.         conn.setRequestProperty(“Connection”, “Keep-Alive”);
  38.         // 得到文件大小
  39.         fileSize = conn.getContentLength();
  40.         conn.disconnect();
  41.         int currentPartSize = fileSize / threadNum + 1;
  42.         RandomAccessFile file = new RandomAccessFile(targetFile, “rw”);
  43.         // 设置本地文件的大小
  44.         file.setLength(fileSize);
  45.         file.close();
  46.         for (int i = 0; i < threadNum; i++) {
  47.             // 计算每条线程的下载的开始位置
  48.             int startPos = i * currentPartSize;
  49.             // 每个线程使用一个RandomAccessFile进行下载
  50.             RandomAccessFile currentPart = new RandomAccessFile(targetFile,
  51.                     “rw”);
  52.             // 定位该线程的下载位置
  53.             currentPart.seek(startPos);
  54.             // 创建下载线程
  55.             threads[i] = new DownloadThread(startPos, currentPartSize,
  56.                     currentPart);
  57.             // 启动下载线程
  58.             threads[i].start();
  59.         }
  60.     }
  61.     /**
  62.      * 获取下载完成的百分比
  63.      *
  64.      * @return
  65.      */
  66.     public double getCompleteRate() {
  67.         // 统计多条线程已经下载的总大小
  68.         int sumSize = 0;
  69.         for (int i = 0; i < threadNum; i++) {
  70.             sumSize += threads[i].length;
  71.         }
  72.         // 返回已经完成的百分比
  73.         return sumSize * 1.0 / fileSize;
  74.     }
  75.     private class DownloadThread extends Thread {
  76.         // 当前线程的下载位置
  77.         private int startPos;
  78.         // 定义当前线程负责下载的文件大小
  79.         private int currentPartSize;
  80.         // 当前线程需要下载的文件块
  81.         private RandomAccessFile currentPart;
  82.         // 定义该线程已下载的字节数
  83.         private int length = 0;
  84.         public DownloadThread(int startPos, int currentPartSize,
  85.                 RandomAccessFile currentPart) {
  86.             this.startPos = startPos;
  87.             this.currentPartSize = currentPartSize;
  88.             this.currentPart = currentPart;
  89.         }
  90.         public void run() {
  91.             try {
  92.                 URL url = new URL(path);
  93.                 HttpURLConnection conn = (HttpURLConnection) url
  94.                         .openConnection();
  95.                 conn.setConnectTimeout(5 * 1000);
  96.                 conn.setRequestMethod(“GET”);
  97.                 conn.setRequestProperty(
  98.                         “Accept”,
  99.                         “image/gif,image/jpeg,image/pjpeg,application/x-shockwaveflash,application/x-ms-xbap,application/xaml+xml,application/vnd.ms-xpsdocument,application/x-ms-application,application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword,*/*”);
  100.                 conn.setRequestProperty(“Accept-Language”, “zh-CN”);
  101.                 conn.setRequestProperty(“Charset”, “UTF-8”);
  102.                 InputStream is = conn.getInputStream();
  103.                 // 跳过startPos个字符,表明该线程只下载自己负责那部分文件
  104.                 is.skip(startPos);
  105.                 byte[] by = new byte[1024];
  106.                 int hasRead = 0;
  107.                 // 读取网络数据,并写入本地文件
  108.                 while (length < currentPartSize
  109.                         && (hasRead = is.read(by)) != -1) {
  110.                     currentPart.write(by, 0, hasRead);
  111.                     // 累计该线程下载的总大小
  112.                     length += hasRead;
  113.                 }
  114.                 currentPart.close();
  115.                 is.close();
  116.             } catch (Exception e) {
  117.                 e.printStackTrace();
  118.             }
  119.         }
  120.     }
  121. }

Activity布局XML:

[html][/html] view plaincopy

  1. <LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
  2.     android:layout_width=”match_parent”
  3.     android:layout_height=”match_parent”
  4.     android:orientation=”vertical” >
  5.     <LinearLayout
  6.         android:layout_width=”match_parent”
  7.         android:layout_height=”wrap_content”
  8.         android:orientation=”horizontal” >
  9.         <TextView
  10.             android:layout_width=”wrap_content”
  11.             android:layout_height=”wrap_content”
  12.             android:text=”输入url:” />
  13.         <EditText
  14.             android:id=”@+id/main_et_url”
  15.             android:layout_width=”match_parent”
  16.             android:layout_height=”wrap_content” />
  17.     </LinearLayout>
  18.     <LinearLayout
  19.         android:layout_width=”match_parent”
  20.         android:layout_height=”wrap_content”
  21.         android:orientation=”horizontal” >
  22.         <TextView
  23.             android:layout_width=”wrap_content”
  24.             android:layout_height=”wrap_content”
  25.             android:text=”输入保存的文件名:” />
  26.         <EditText
  27.             android:id=”@+id/main_et_name”
  28.             android:layout_width=”match_parent”
  29.             android:layout_height=”wrap_content” />
  30.     </LinearLayout>
  31.     <Button
  32.         android:id=”@+id/main_btn_download”
  33.         android:layout_width=”wrap_content”
  34.         android:layout_height=”wrap_content”
  35.         android:text=”下载” />
  36.     <ProgressBar
  37.         android:id=”@+id/main_progressBar”
  38.         style=”@android:style/Widget.ProgressBar.Horizontal”
  39.         android:layout_width=”match_parent”
  40.         android:layout_height=”wrap_content” />
  41. </LinearLayout>

权限:

[html][/html] view plaincopy

  1. <!– 在SD卡中创建与删除文件权限 –>
  2. <uses-permission android:name=”android.permission.MOUNT_UNMOUNT_FILESYSTEMS”/>
  3. <!– 向SD卡写入数据权限 –>
  4. <uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”/>
  5. <!– 授权访问网络 –>
  6. <uses-permission android:name=”android.permission.INTERNET”/>

标签