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. 初识redis——mac下搭建redis环境

    一.redis简介 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合)和zset(有 ...

  2. 用Jquery获取select的value和text值

    $("#select_id").change(function(){//code...}); //为Select添加事件,当选择其中一项时触发 var checkText=$(&q ...

  3. C语言语法之占用字节

    指针占用字节 指针即为地址,存的是变量的地址,在同一架构下地址长度都是相同的(cpu的最大寻址内存空间),所以不同类型的指针长度都一样. 指针占用几个字节跟语言无关,而是跟系统的寻址能力有关,16为地 ...

  4. C++—函数

    一.函数的基本知识 要使用C++函数,必须完成一下工作: (1)提供函数定义: (2)提供函数原型: (3)调用函数. 1.定义函数 可以将函数分为两类,有返回值的函数和没有返回值的函数.没有返回值的 ...

  5. 第四章 Python外壳:代码结构

    Python的独特语法: 不使用分号结束语句,而是回车: 通过代码缩进来区分代码块: if.while.for等,都不用括号,但不能没有冒号(:). 如何将一行命令分为多行? >>> ...

  6. 【转】PackageInfo、ResolveInfo 笔记

    1.PackageInfo.ResolveInfo PackageItemInfo:包含了一些信息的基类, 它的直接子类有: ApplicationInfo. ComponentInfo.Instru ...

  7. iOS 静态类库 打包 C,C++文件及和OC混编

    iOS 静态类库 编译 C,C++ 我们都知道,OC 原生支持C, 在 创建的 OC类的 .m 里面,可以直接编写C的代码: 同样 Xcode 也支持 OC ,C++的混编,此时,我们通常把OC创建的 ...

  8. 【转】 简单理解Socket

    题外话 前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公司使用的一些控件的开发,浏览器兼容性搞死人:但主要是因为这段时间一直在看html5的东西,看到web socket时觉得很有 ...

  9. UIStepper

    @在IOS5中增加了一个UIStepper的新控件,UIStepper可以连续增加或减少一个数值.控件的外观是两个水平并排的按钮构成,一个显示为“+”,一个显示为“-”. 1. 初始化控件 UISte ...

  10. navigator.userAgent.indexOf来判断浏览器类型

    navigator.userAgent.indexOf来判断浏览器类型 (2011-03-03 11:30:40) 转载▼ 标签: 杂谈   来源:http://xtaai5233.blog.163. ...