Android 使用HttpURLConnection实现多线程下载 笔记

参考阅读文章:

http://www.tuicool.com/articles/7bEJFv



使用HttpURLConnection实现多线程下载  核心步骤:



*****************源代码及注释****************

DownUtil.java

package com.example.multithreaddown;

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

public class DownUtil {
	
	private String path;
	private String targetFile;
	private int ThreadNum;
	
	private DownThread[] threads;//downthread object  array
	
	private int total_filesize;//get length  , calculate down percentage 
	
	public DownUtil(String path,String targetFile,int ThreadNum){//outclass constructor
		this.path = path;
		this.targetFile = targetFile;
		this.ThreadNum = ThreadNum;
		threads = new DownThread[ThreadNum];//initial download object array
	}
	
	//main download method  create each downthread object
	public void download() throws Exception{
		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-shockwave-flash, 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");//**UTF-8***in android platform 
		conn.setRequestProperty("Connection", "Keep-Alive");

		//
		total_filesize = conn.getContentLength();
		conn.disconnect();
		
		RandomAccessFile file = new RandomAccessFile(targetFile,"rw");
		file.setLength(total_filesize);
		file.close();
		
		int currentPartSize = total_filesize/ThreadNum + 1;
		int startPos = 0;
		
		//create each download thread
		for(int i = 0;i<ThreadNum;i++){
			startPos = i*currentPartSize;
			
			//***
			RandomAccessFile currentPart = new RandomAccessFile(targetFile,"rw");
			currentPart.seek(startPos);
			
			threads[i] = new DownThread(startPos,currentPartSize,currentPart);
			
			//
			threads[i].start();//
			
		}
		
	}
	
	public double getCompleteRate(){
		int sumSize = 0;
		for(int i = 0;i<ThreadNum;i++){
			//calculate the already download amount of each thread
			sumSize += threads[i].download_length;
		}
		return (sumSize*1.0)/total_filesize;
	}
	
	//Inner thread class
	private class DownThread extends Thread{
		//
		private int startPos;
		private int currentPartSize;
		private RandomAccessFile currentPart;
		
		public int download_length = 0;//calculate percentage in outer class method
		//inner class constructor
		public DownThread(int startPos,int currentPartSize,RandomAccessFile currentPart){
			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.setConnectTimeout(5 * 1000);
				conn.setRequestMethod("GET");
				conn.setRequestProperty(
					"Accept",
					"image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
					+ "application/x-shockwave-flash, 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.setRequestProperty("Connection", "Keep-Alive");

				//
				InputStream inStream = conn.getInputStream();
				inStream.skip(startPos);//
				byte []buffer = new byte[1024];
				int hasRead = 0;
			
				//
			//	while((download_length<=currentPartSize)&&(hasRead = inStream.read(buffer))!=-1){
				while((download_length<currentPartSize)&&(hasRead = inStream.read(buffer))!=-1){
					currentPart.write(buffer, 0, hasRead);//
					download_length += hasRead;
				}
				currentPart.close();
				inStream.close();
				
				
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
}

MultiThreadDown.java

package com.example.multithreaddown;

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

public class DownUtil {
	
	private String path;
	private String targetFile;
	private int ThreadNum;
	
	private DownThread[] threads;//downthread object  array
	
	private int total_filesize;//get length  , calculate down percentage 
	
	public DownUtil(String path,String targetFile,int ThreadNum){//outclass constructor
		this.path = path;
		this.targetFile = targetFile;
		this.ThreadNum = ThreadNum;
		threads = new DownThread[ThreadNum];//initial download object array
	}
	
	//main download method  create each downthread object
	public void download() throws Exception{
		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-shockwave-flash, 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");//**UTF-8***in android platform 
		conn.setRequestProperty("Connection", "Keep-Alive");

		//
		total_filesize = conn.getContentLength();
		conn.disconnect();
		
		RandomAccessFile file = new RandomAccessFile(targetFile,"rw");
		file.setLength(total_filesize);
		file.close();
		
		int currentPartSize = total_filesize/ThreadNum + 1;
		int startPos = 0;
		
		//create each download thread
		for(int i = 0;i<ThreadNum;i++){
			startPos = i*currentPartSize;
			
			//***
			RandomAccessFile currentPart = new RandomAccessFile(targetFile,"rw");
			currentPart.seek(startPos);
			
			threads[i] = new DownThread(startPos,currentPartSize,currentPart);
			
			//
			threads[i].start();//
			
		}
		
	}
	
	public double getCompleteRate(){
		int sumSize = 0;
		for(int i = 0;i<ThreadNum;i++){
			//calculate the already download amount of each thread
			sumSize += threads[i].download_length;
		}
		return (sumSize*1.0)/total_filesize;
	}
	
	//Inner thread class
	private class DownThread extends Thread{
		//
		private int startPos;
		private int currentPartSize;
		private RandomAccessFile currentPart;
		
		public int download_length = 0;//calculate percentage in outer class method
		//inner class constructor
		public DownThread(int startPos,int currentPartSize,RandomAccessFile currentPart){
			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.setConnectTimeout(5 * 1000);
				conn.setRequestMethod("GET");
				conn.setRequestProperty(
					"Accept",
					"image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
					+ "application/x-shockwave-flash, 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.setRequestProperty("Connection", "Keep-Alive");

				//
				InputStream inStream = conn.getInputStream();
				inStream.skip(startPos);//
				byte []buffer = new byte[1024];
				int hasRead = 0;
			
				//
			//	while((download_length<=currentPartSize)&&(hasRead = inStream.read(buffer))!=-1){
				while((download_length<currentPartSize)&&(hasRead = inStream.read(buffer))!=-1){
					currentPart.write(buffer, 0, hasRead);//
					download_length += hasRead;
				}
				currentPart.close();
				inStream.close();
				
				
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
}

activity_multi_thread_down.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	>
<TextView
	android:layout_width="match_parent" 
	android:layout_height="wrap_content"
	android:text="下载资源的URL:"
	/>
<EditText
	android:id="@+id/url"  
	android:layout_width="match_parent" 
	android:layout_height="wrap_content"
	android:text="http://zhangmenshiting.baidu.com/data2/music/108215782/14385500158400128.mp3?xcode=403698f0ca4477d527224dddfa01e691cc816ddc4bb4461a"
	/>
<TextView
	android:layout_width="match_parent" 
	android:layout_height="wrap_content"
	android:text="目标文件:"
	/>
<EditText
	android:id="@+id/target"  
	android:layout_width="match_parent" 
	android:layout_height="wrap_content"
	android:text="/mnt/sdcard/时间都去哪儿了.mp3"
	/>	
<TextView
	android:layout_width="match_parent" 
	android:layout_height="wrap_content"
	android:text="线程数:"
	/>

<NumberPicker
			android:id="@+id/np_thread"
			android:layout_width="match_parent"
			android:layout_height="80dp"
			android:focusable="true"
			android:focusableInTouchMode="true" />

<Button
	android:id="@+id/down"  
	android:layout_width="wrap_content" 
	android:layout_height="wrap_content"
	android:layout_gravity="center"
	android:text="下载"
	/>
<!-- 定义一个水平进度条,用于显示下载进度 -->
<ProgressBar
	android:id="@+id/bar"
 	android:layout_width="match_parent" 
	android:layout_height="20dp"
	android:max="100"
	style="?android:attr/progressBarStyleHorizontal"
	/>
</LinearLayout>


***********************************************

注释:

1.核心代码:

首先创建一个文件指针file指向将要写的文件,设置该文件的大小,然后关闭该文件指针。

接着在创建不同线程的for()循环中让不同线程的new currentPart文件指针指向该文件的不同部分;让不同线程写该文件的不同部分。




2.核心Java  IO API支持:

(1)

public class RandomAccessFile
extends Object
implements DataOutput, DataInput, Closeable

此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过getFilePointer 方法读取,并通过seek 方法设置。

构造方法:

RandomAccessFile(String name,String mode)
创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。

方法摘要

void seek(long pos) 
设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。

void write(byte[] b, int off, int len) 
将 len 个字节从指定 byte 数组写入到此文件,并从偏移量 off 处开始。

void setLength(long newLength) 
设置此文件的长度。

(2)

java.io 
类 InputStream

long skip(long n) 
跳过和丢弃此输入流中数据的 n 个字节。


3. 计算每条线程需要下载的数据长度的原理及影响的详细分析:

 * 1 如果资源大小模于线程数时结果为0,那么表示每条线程需要下载的大小恰好将原大小等分

 * 2 当然更多的情况是有余数的(即不能整除).那么此时该怎么办呢?每条线程该下载的长度是多少呢?

 *   我们可以这么做:

(1)原大小/除以线程的条数

 (2) 1的基础上+1

  这样就表示每条线程要下载的大小长度


4.需要添加的相关权限

<!-- 在SD卡中创建与删除文件权限-->

    <uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

    <!--向SD卡写入数据权限 -->

    <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <!--授权访问网络 -->

    <uses-permissionandroid:name="android.permission.INTERNET"/>


5.注意:

线程中不能处理UI操作  比如toast 

要处理UI操作都只能通知Handler  更新用户UI界面


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值