使用HTTP访问网络资源
前面介绍了 URLConnection己经可以非常方便地与指定站点交换信息,URLConnection还有一个子类:HttpURLConnection,HttpURLConnection 在 LIRLConnection的基础上做了进一步改进,增加了一些用于操作http资源的便捷方法。
1.使用HttpURLConnection
HttpURLConnection继承了URLConnection,因此也可用于向指定网站发送GET请求 POST请求。它在URLConnection的基础上提供了如下便捷的方法。
1)
Int getResponseCode():获取服务器的响应代码。
2)
String getResponseMessage():获取服务器的响应消息。
3)
String getRequestMethod():获取发送请求的方法。
4)
void setRequestMethod(String method):设置发送请求的方法。
下面通过个实用的示例来示范使用HttpURLConnection实现多线程下载。
1.1实例:多线程下载
使用多线程下载文件可以更快地完成文件的下载,因为客户端启动多个线程进行下寒意味着服务器也需要为该客户端提供相应的服务。假设服务器同时最多服务100个用户,服务器中一条线程对应一个用户,100条线程在计算机内并发执行,也就是由CPU划分史 片轮流执行,如果A应用使用了 99条线程下载文件,那么相当于占用了 99个用户的资源自然就拥有了较快的下载速度。
提示:实际上并不是客户端并发的下载线程越多,程序的下载速度就越快,因为当客户端开启太多的并发线程之后,应用程序需要维护每条线程的开销、线程同步的开销,这些开销反而会导致下载速度降低.
1.2为了实现多线程下载,程序可按如下步骤进行:
?
创建URL对象。
?
获取指定URL对象所指向资源的大小(由getContentLength()方法实现),此处用了 HttpURLConnection 类。
?
在本地磁盘上创建一个与网络资源相同大小的空文件。
?
计算每条线程应该下载网络资源的哪个部分(从哪个字节开始,到哪个字节结束,依次创建、启动多条线程来下载网络资源的指定部分。
1.2该程序提供的下载工具类代码如下。
package com.jph.net;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* Description:
* 创建ServerSocket监听的主类
* @author jph
* Date:2014.08.27
*/
public class DownUtil
{
/**下载资源的路径**/
private String path;
/**下载的文件的保存位置**/
private String targetFile;
/**需要使用多少线程下载资源**/
private int threadNum;
/**下载的线程对象**/
private DownThread[] threads;
/**下载的文件的总大小**/
private int fileSize;
public DownUtil(String path, String targetFile, int threadNum)
{
this.path = path;
this.threadNum = threadNum;
// 初始化threads数组
threads = new DownThread[threadNum];
this.targetFile = targetFile;
}
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");
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 DownThread(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 DownThread extends Thread
{
/**当前线程的下载位置**/
private int startPos;
/**定义当前线程负责下载的文件大小**/
private int currentPartSize;
/**当前线程需要下载的文件块**/
private RandomAccessFile currentPart;
/**定义该线程已下载的字节数**/
public int length;
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");
InputStream inStream = conn.getInputStream();
// 跳过startPos个字节,表明该线程只下载自己负责哪部分文件。
inStream.skip(this.startPos);
byte[] buffer = new byte[1024];
int hasRead = 0;
// 读取网络数据,并写入本地文件
while (length < currentPartSize
&& (hasRead = inStream.read(buffer)) > 0)
{
currentPart.write(buffer, 0, hasRead);
// 累计该线程下载的总大小
length += hasRead;
}
currentPart.close();
inStream.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
上而的DownUtil工具类中包括一个DownloadThread内部类,该内部类的run()方法中负责打开远程资源的输入流,并调用inputStream的skip(int)方法跳过指定数量的字节,这样就让该线程读取由它自己负责下载的部分。
提供了上面的DownUtil工具类之后,接下来就可以在Activity中调用该DownUtil类来执行下载任务,该程序界面中包含两个文本框,一个用于输入网络文件的源路径,另一个用于指定下载到本地的文件的文件名,该程序的界面比较简单,故此处不再给出界面布局代码。该程序的Activity代码如下。
package com.jph.net;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
/**
* Description:
* 多线程下载
* @author jph
* Date:2014.08.27
*/
public class MultiThreadDown extends Activity
{
EditText url;
EditText target;
Button downBn;
ProgressBar bar;
DownUtil downUtil;
private int mDownStatus;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 获取程序界面中的三个界面控件
url = (EditText) findViewById(R.id.url);
target = (EditText) findViewById(R.id.target);
downBn = (Button) findViewById(R.id.down);
bar = (ProgressBar) findViewById(R.id.bar);
// 创建一个Handler对象
final Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
if (msg.what == 0x123)
{
bar.setProgress(mDownStatus);
}
}
};
downBn.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
// 初始化DownUtil对象(最后一个参数指定线程数)
downUtil = new DownUtil(url.getText().toString(),
target.getText().toString(), 6);
new Thread()
{
@Override
public void run()
{
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)
{
showToastByRunnable(MultiThreadDown.this, "下载完成", 2000);
timer.cancel();
}
}
}, 0, 100);
}
}.start();
}
});
}
/**
* 在非UI线程中使用Toast
* @param context 上下文
* @param text 用以显示的消息内容
* @param duration 消息显示的时间
* */
private void showToastByRunnable(final Context context, final CharSequence text, final int duration) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, text, duration).show();
}
});
}
}
上面的Activity不仅使用了 DownUtil来控制程序下载,而且程序还启动了一个定时器,该定时器控制每隔0.1秒