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

1、int  getResponseCode():获取服务器的响应代码。

2、String  getResponseMessage():获取服务器的响应消息。

3、String  getRequestMethod():获取发送请求的方法。

4、void  setRequestMethod(String  method):设置发送请求的方法。

下面通过一个实用的示例来示范使用HTTPURLConnection实现多线程下载:

使用多线程下载文件可以更快的完成文件的下载,因为客户端启动多个线程进行下载就意味着服务器也需要为该客户端提供相应的服务。假设服务器同时最多服务100个用户,在服务器中一条线程对应一个用户,100条线程在计算机内并发执行,也就是由CPU划分时间片轮流执行,如果A应用使用了99条线程下载文件,那么相同于占用了99个用户的资源,自然就拥有了较快的下载速度。

注:实际上并不是客户端并发的下载线程越多,程序的下载速度就越快,因为当客户端开启太多的并发线程之后,应用程序需要维护每条线程的开销、线程同步的开销,这些开销反而会导致下载速度降低。

为了实现多线程,程序可按如下步骤进行:

1、创建URL对象。

2、获取指定URL对象所指向资源的大小(由getContentLength()方法实现),此处用到了HTTPURLConnection类。

3、在本地磁盘上创建一个与网络资源相同大小的空文件。

4、计算每条线程应该下载网络资源的哪个部分(从哪个字节开始,到哪个字节结束).

5、依次创建、启动多条线程来下载网络资源的指定部分。

该程序提供的下载工具类:

import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class DownUtil {
  //定义下载资源的路径
  private String path;
  //指定所下载的文件的保存位置
  private String targetFile;
  //定义需要使用多少线程下载资源
  private int threadNum;
  //定义下载的线程对象
  private DownloadThread[] threads;
  //定义下载的文件的总大小
  private int fileSize;
  public DownUtil(String path, String targetFile, int threadNum) {
    super();
    this.path = path;
    this.targetFile = targetFile;
    this.threadNum = threadNum;
    //初始化threads数组
    threads = new DownloadThread[threadNum];
  }
  public void download() throws IOException{
    URL url = new URL(path);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setConnectTimeout(5*1000);
    conn.setRequestMethod("GET");
    conn.setRequestProperty("Accept",
        "image/gif,image/jpeg,image/pjpeg,image/pjpeg,application/x-shockwaveflash" +
        "application/xaml+xml,application/vnd.ms-xpsdocument,application/x-ms-xbap" +
        "application/x-ms-application,application/vnd.ms-excel,application/vnd.ms-powerpoint" +
        "application/msword,*/*");
    conn.setRequestProperty("Accept-Language", "zh-CN");
    conn.setRequestProperty("Charset", "UTF-8");
    conn.addRequestProperty("User-Agent",
        "Mozila/4.0(compatible;MSIE 7.0;Windows NT 5.2;Trident/4.0;" +
        ".NET CLR 1.1.4322;.NET CLR 2.0.5.727;.NET CLR 3.0.04506;.NET CLR " +
        "3.0.4506.2152;.NET CLR 3.5.30729)");
    conn.setRequestProperty("Connection", "Keep-Alive");
    //得到文件大小
    fileSize = conn.getContentLength();
    conn.disconnect();
    int currentPartSize = fileSize/threadNum +1;
    RandomAccessFile file = new RandomAccessFile(targetFile, "rw");
    //设置本地文件的大小
    file.setLength(fileSize);
    file.close();
    for(int i =0 ;i<threadNum ;i++){
      //计算每条线程的下载的开始位置
      int startPos = i * currentPartSize;
      //每个线程使用一个RandomAccessFile进行下载
      RandomAccessFile currentPart = new RandomAccessFile(
            targetFile, "rw");
      //定位该线程的下载位置
      currentPart.seek(startPos);
      //创建下载线程
      threads[i] = new DownloadThread(startPos, currentPartSize, currentPart);
      //启动下载线程
      threads[i].start();
    }
  }
  //获取下载的完成百分比
  public double getCompleteRate(){
    //统计多条线程已经下载的总大小
    int sumSize = 0;
    for(int i =0 ;i<threadNum;i++){
      sumSize += threads[i].length;
    }
    //返回已经完成的百分比
    return sumSize*1.0/fileSize;
  }

  private class DownloadThread extends Thread{
    //当前线程的下载位置
    private int startPos;
    //定义当前线程负责下载的文件大小
    private int currentPartSize;
    //当前线程需要下载的文件块
    private RandomAccessFile currentPart;
    //定义已经该线程已下载的字节数
    public int length;
    public DownloadThread(int startPos, int currentPartSize,
              RandomAccessFile currentPart) {
      super();
      this.startPos = startPos;
      this.currentPartSize = currentPartSize;
      this.currentPart = currentPart;
    }
    @Override
    public void run() {
      try {
        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(5*1000);
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Accept",
            "image/gif,image/jpeg,image/pjpeg,image/pjpeg,application/x-shockwaveflash" +
            "application/xaml+xml,application/vnd.ms-xpsdocument,application/x-ms-xbap" +
            "application/x-ms-application,application/vnd.ms-excel,application/vnd.ms-powerpoint" +
            "application/msword,*/*");
        conn.setRequestProperty("Accept_Language", "zh-CN");
        conn.setRequestProperty("Charset", "UTF-8");
        InputStream inStream = conn.getInputStream();
        //跳过startPos个字节,表明该线程只下载自己负责那部分文件
        inStream.skip(this.startPos);
        byte[] buffer = new byte[1024];
        int hasRead = 0;
        //读取网络数据,并写人本地文件
        while(length < currentPartSize && (hasRead = inStream.read(buffer)) != -1){
                currentPart.write(buffer , 0 , hasRead);
          //累计该线程下载的总大小
          length += hasRead;
        }
      currentPart.close();
      inStream.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    super.run();
  }

  }
}

上面的DownUtil工具类中包括一个DownloadThread内部类,该内部类run()方法中负责打开远程资源的输入流,并调用InputStream的skip(int)方法跳过指定数量的字节,这样就让该线程读取由它自己负责的部分,提供了上面的DownUtil工具类之后,接下来就可以在Activity中调用该DownUtility类来执行下载任务了。

import java.util.Timer;
import java.util.TimerTask;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.SeekBar;

public class MutilThreadDown extends Activity {
  EditText url;
  EditText target;
  Button downBtn;
  SeekBar bar;
  DownUtil downUtil;
  private int mDownStatus;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_mutil_thread_down);
    //获取程序界面中的三个界面控件
    url = (EditText) findViewById(R.id.url);
    target = (EditText) findViewById(R.id.target);
    downBtn = (Button) findViewById(R.id.downBtn);
    bar = (SeekBar) findViewById(R.id.bar);
    //创建一个Handler对象
    final Handler handler = new Handler(){
      @Override
      public void handleMessage(Message msg) {
        if(msg.what == 0x123){
          bar.setProgress(mDownStatus);
        }
      }
    };
    downBtn.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        // 初始化DownUtil对象
        downUtil = new DownUtil(url.getText().toString(),
        target.getText().toString(), 4);
        try {
          //开始下载
          downUtil.download();
        } catch (Exception e) {
          e.printStackTrace();
        }
        //定义每秒调度获取一次系统的完成进度
        final Timer timer = new Timer();
        timer.schedule(new TimerTask() {

          @Override
          public void run() {
            // 获取下载任务的完成比率
            double completeRate = downUtil.getCompleteRate();
            mDownStatus = (int)(completeRate*100);
            //发送消息通知界面更新进度条
            handler.sendEmptyMessage(0x123);
            //下载完成后取消任务调度
            if(mDownStatus >= 100){
              timer.cancel();
            }
          }
        }, 0, 100);
      }
    });
  }

}

上面的Activity不仅使用了DownUtil来控制程序下载,而且程序还启动了一个定时器,该定时器控制每隔0.1秒查询一次下载进度,并通过程序中的进度条来显示任务的下载进度。

该程序不仅需要访问网络,还需要访问系统SD卡,在SD卡中创建文件,因此必须授予程序访问网络、访问SD卡文件的权限,也就是AndroidManifest.xml文件中增加如下配置:

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

注:上面的程序已经实现了多线程下载的核心代码,如果要实现断点下载,则还需要额外增加一个配置文件(所有断点下载工具都会在下载开始生成两个文件:一个是与网络资源相同大小的空文件,一个是配置文件),该配置文件分别记录每个线程已经下载到了那个字节,当网络断开后再次开始下载时,每个线程根据配置文件里记录的位置向后下载即可。

使用HTTP访问网络------使用HTTPURLConnection的更多相关文章

  1. 10_Android中通过HttpUrlConnection访问网络,Handler和多线程使用,读取网络html代码并显示在界面上,ScrollView组件的使用

     编写如下项目: 2 编写Android清单文件 <?xml version="1.0" encoding="utf-8"?> <mani ...

  2. HttpConnection方式访问网络

    参考疯狂android讲义,重点在于学习1.HttpConnection访问网络2.多线程下载文件的处理 主activity: package com.example.multithreaddownl ...

  3. Android 使用 HTTP 协议访问网络

    正在看<第一行代码>,记录一下使用 HTTP 协议访问网络的内容吧! 在Android发送Http请求有两种方式,HttpURLConnection和HttpClient. 1.使用Htt ...

  4. android post 方式 访问网络 实例

    android post 方式 访问网络 实例 因为Android4.0之后对使用网络有特殊要求,已经无法再在主线程中访问网络了,必须使用多线程访问的模式 该实例需要在android配置文件中添加 网 ...

  5. Android使用Http协议访问网络——HttpConnection

    套路篇 使用HttpConnection访问网络一般有如下的套路: 1.获取到HttpConnection的实例,new出一个URL对象,并传入目标的网址,然后调用一下openConnection() ...

  6. 使用HTTP协议访问网络(Android)

    在做项目的过程中需要连接服务器访问数据,还没有接触过Android网络编程方面,参考了<Android第一行代码>,在做的过程中遇到了很多的问题,这里就此记录一下. 先给出访问网络的代码: ...

  7. HttpClient访问网络

    HttpClient项目时Apache提供用于访问网络的类,对访问网络的方法进行了封装.在HttpURlConnection类中的输入输出操作,统一封装成HttpGet.HttpPost.HttpRe ...

  8. iOS HTTP访问网络受限

    HTTP访问网络受限,只需要在项目工程里的Info.plist添加 <key>NSAppTransportSecurity</key> <dict> <key ...

  9. android 使用httpclient访问网络

    在主活动类中,调用一个线程访问网络(android4.0以上耗时的操作不能放在主线程中):       //声明两个Button对象,与一个TextView对象private TextView mTe ...

随机推荐

  1. POCO C++库学习和分析——任务

    1.任务的定义 任务虽然在Poco::Foundation库的目录中被单独划出来,其实可以被看成线程的应用,放在线程章节.首先来看一下Poco中对任务的描述: *task主要应用在GUI和Sever程 ...

  2. GIT过滤

    git 创建 .gitignore 文件 建立项目过滤规则 创建 .gitignore 随意设置想跟踪哪些文件 和不跟踪哪些文件. 1.在项目根目录下建立 .gitignore 文件 2.   .gi ...

  3. mybatis动态SQL中的sql片段

    在mybatis中通过使用SQL片段可以提高代码的重用性,如下情景: 1.创建动态SQL <sql id="sql_count">select count(*)< ...

  4. 使用 Velocity 模板引擎快速生成代码(zhuan)

    http://www.ibm.com/developerworks/cn/java/j-lo-velocity1/ ****************************************** ...

  5. Linux计划任务Crontab实例详解教程

    说明:Crontab是Linux系统中在固定时间执行某一个程序的工具,类似于Windows系统中的任务计划程序 下面通过详细实例来说明在Linux系统中如何使用Crontab 操作系统:CentOS ...

  6. ORA-12705: Cannot access NLS data files or invalid environment specified

    ASM实例无法启动 [grid@data ~]$ sqlplus / as sysasm SQL*Plus: Release 11.2.0.4.0 Production on Fri Sep 11 0 ...

  7. Java 数据类型之间的转换 拆分字符串 Date/Calendar的转换

    数据类型转换 1. String - Int String str="123"; int i=1; int str=Integer.parseInt(str); String i= ...

  8. PHP 安全相关 简单知识

    概要: 1.php一些安全配置 (1)关闭php提示错误功能 (2)关闭一些“坏功能” (3)严格配置文件权限. 2.严格的数据验证,你的用户不全是“好”人 2.1为了确保程序的安全性,健壮性,数据验 ...

  9. java final

    final:(最终的)看不懂时有必要分析内存画图,不同方法的局部变量是相互独立的额不要被所起的名所困扰. 1)每个方法运行时jvm,都会为其开辟一片内存空间.内存空间是属于这个方法的, 同时,方法中的 ...

  10. OneProxy读写分离配置操作手册

    1.确保已配置好主备集群 A)配置 可参考MySQL官方文档(https://dev.mysql.com/doc/refman/5.6/en/replication-howto.html) 或者我的博 ...