HttpClient
这个不用说了,Apache的API,但是不推荐使用了,在最新的api中甚至都把HttpClient去掉了。但是还是有必要掌握下HttpClient的用法的
GET方式
//先将参数放入List,再对参数进行URL编码
List<BasicNameValuePair> params = new LinkedList<BasicNameValuePair>();
params.add(new BasicNameValuePair("param1", "中国"));
params.add(new BasicNameValuePair("param2", "value2"));
//对参数编码
String param = URLEncodedUtils.format(params, "UTF-8");
//baseUrl
String baseUrl = "http://ubs.free4lab.com/php/method.php";
//将URL与参数拼接
HttpGet getMethod = new HttpGet(baseUrl + "?" + param);
HttpClient httpClient = new DefaultHttpClient();
try {
HttpResponse response = httpClient.execute(getMethod); //发起GET请求
Log.i(TAG, "resCode = " + response.getStatusLine().getStatusCode()); //获取响应码
Log.i(TAG, "result = " + EntityUtils.toString(response.getEntity(), "utf-8"));//获取服务器响应内容
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
POST方法
//和GET方式一样,先将参数放入List
params = new LinkedList<BasicNameValuePair>();
params.add(new BasicNameValuePair("param1", "Post方法"));
params.add(new BasicNameValuePair("param2", "第二个参数"));
try {
HttpPost postMethod = new HttpPost(baseUrl);
postMethod.setEntity(new UrlEncodedFormEntity(params, "utf-8")); //将参数填入POST Entity中
HttpResponse response = httpClient.execute(postMethod); //执行POST方法
Log.i(TAG, "resCode = " + response.getStatusLine().getStatusCode()); //获取响应码
Log.i(TAG, "result = " + EntityUtils.toString(response.getEntity(), "utf-8")); //获取响应内容
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
HttpClient传递复杂的参数,比如文件 需要额外的jar包
常见问题
异常处理
协议异常ClientProtocolException
: 无效的cookie 很多http请求需要提供登录凭证 如果没有则协议异常
连接超时SocketTimeoutException
: 无法连接到服务器比如服务器不可用 那么就连接超时
套接字超时ConnectTimeoutException
: 既定时间内未收到服务器响应此时可以连接到服务器 但是未收到响应 那么套接字超时
HttpConnectionparam.setconnectiontimeout(param,5000) //连接超时时间
HttpConnectionparam.setsotimeout(param,5000) //套接字连接超时
ConnManagerParams.settimeout(param,5000) //连接管理器的超时 定义应用程序等待多久才让一个连接退出连接池的管理 比如连接池已经满了 新的连接就必须要等待了
使用try catch块来捕获
多线程问题
可以设置单例的httpclient 这样就可以共享设置的连接参数,但是单例的话 多线程就会有问题
HttpClient httpClient = new DefaultHttpClient(); 有多线程问题
比如两个线程都调用httpClient.execute(getMethod); getMethod使用的是同一个HttpGet对象 那么毫无疑问多线程问题
HttpConnectionparam.setconnectiontimeout(params,5000)
HttpConnectionparam.setsotimeout(params,5000)
ConnManagerParams.settimeout(params,5000)
HttpClient httpClient = new DefaultHttpClient(ThreadSafeClientManager conMgr,HttpParams params);
ThreadSafeClientManager
负责管理所有的httpclient的http连接 内部实现不会有多线程问题
此时的HttpParams作用在HttpClient上,所以可以供所有的HttpGet HttpPost对像共享
如果要定义自己的连接参数 那么可以
HttpGet getMethod = new HttpGet(baseUrl + "?" + param);
HttpParams params= getMethod.getarams();
HttpConnectionparam.setconnectiontimeout(param,5000);
getMethod.setparams(params);
此时的HttpParams作用在HttpGet上,不会影响到HttpClient共享的连接参数
AndroidHttpClient
HttpClient子类 适用于android开发人员 连接超时/套接字超时默认20s
默认是线程安全的ThreadSafeClientManager
HttpUrlConnection
HttpUrlConnection属于Java的标准类库
更轻量了 推荐使用这个 4.4开始底层通过Okhttp来实现
HttpURLConnection的多线程问题? 因为采取Okhttp的底层实现,是木有多线程的问题的,具体是为什么,需要研究
HttpURLConnection使用
1. HttpURLConnection连接URL
1)创建一个URL对象
URL url = new URL(http://www.baidu.com);
2)利用HttpURLConnection对象从网络中获取网页数据
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
3)设置连接超时
conn.setConnectTimeout(6*1000);
4)对响应码进行判断
if (conn.getResponseCode() != 200) //从Internet获取网页,发送请求,将网页以流的形式读回来
throw new RuntimeException(“请求url失败”);
5)得到网络返回的输入流
InputStream is = conn.getInputStream();
6)String result = readData(is, “GBK”); //文件流输入出文件用outStream.write
7)conn.disconnect();
总结:
- 记得设置连接超时,如果网络不好,Android系统在超过默认时间会收回资源中断操作.
- 返回的响应码200,是成功.
- 在Android中对文件流的操作和JAVA SE上面是一样的.
- 在对大文件的操作时,要将文件写到SDCard上面,不要直接写到手机内存上.
- 操作大文件是,要一遍从网络上读,一遍要往SDCard上面写,减少手机内存的使用.
- 对文件流操作完,要记得及时关闭.
2. 向Internet发送请求参数
步骤:
1)创建URL对象:URL realUrl = new URL(requestUrl);
2)通过HttpURLConnection对象,向网络地址发送请求
HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
3)设置容许输出:conn.setDoOutput(true);
4)设置不使用缓存:conn.setUseCaches(false);
5)设置使用POST的方式发送:conn.setRequestMethod(“POST”);
6)设置维持长连接:conn.setRequestProperty(“Connection”, “Keep-Alive”);
7)设置文件字符集:conn.setRequestProperty(“Charset”, “UTF-8”);
8)设置文件长度:conn.setRequestProperty(“Content-Length”, String.valueOf(data.length));
9)设置文件类型:conn.setRequestProperty(“Content-Type”,”application/x-www-form-urlencoded”);
10)以流的方式输出.
总结:
- 发送POST请求必须设置允许输出
- 不要使用缓存,容易出现问题.
- 在开始用HttpURLConnection对象的setRequestProperty()设置,就是生成HTML文件头.
3. 向Internet发送xml数据
XML格式是通信的标准语言,Android系统也可以通过发送XML文件传输数据.
1)将生成的XML文件写入到byte数组中,并设置为UTF-8:byte[] xmlbyte = xml.toString().getBytes(“UTF-8”);
2)创建URL对象,并指定地址和参数:URL url = new URL(http://localhost:8080/itcast/contanctmanage.do?method=readxml);
3)获得链接:HttpURLConnection conn = (HttpURLConnection) url.openConnection();
4)设置连接超时:conn.setConnectTimeout(6* 1000);
5)设置允许输出conn.setDoOutput(true);
6)设置不使用缓存:conn.setUseCaches(false);
7)设置以POST方式传输:conn.setRequestMethod(“POST”);
8)维持长连接:conn.setRequestProperty(“Connection”, “Keep-Alive”);
9)设置字符集:conn.setRequestProperty(“Charset”, “UTF-8”);
10)设置文件的总长度:conn.setRequestProperty(“Content-Length”, String.valueOf(xmlbyte.length));
11)设置文件类型:conn.setRequestProperty(“Content-Type”, “text/xml; charset=UTF-8”);
12)以文件流的方式发送xml数据:outStream.write(xmlbyte);
总结:
- 我们使用的是用HTML的方式传输文件,这个方式只能传输一般在5M一下的文件.
- 传输大文件不适合用HTML的方式,传输大文件我们要面向Socket编程.确保程序的稳定性
- 将地址和参数存到byte数组中:byte[] data = params.toString().getBytes();
HttpClient还是HttpUrlConnection
参考 Android访问网络,使用HttpURLConnection还是HttpClient?
在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。
而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择。它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。对于新的应用程序应该更加偏向于使用HttpURLConnection,因为在以后的工作当中我们也会将更多的时间放在优化HttpURLConnection上面。
其实现在嘛,两者都不用,就用Okhttp
HttpUrlConnection现在的底层实现就是通过Okhttp
HttpURLConnection java的标准类(java.net),什么都没封装,用起来太原始,不方便
HttpClient Apache开源框架,提供对HTTP协议访问的封装,包括http的请求头,参数,内容体,响应等及多线程的应用。
HttpClient方便,但是封装了太多,也可能导致网络请求速度变慢,灵活度不够
HttpClient就是一个增强版的HttpURLConnection ,HttpURLConnection可以做的事情 HttpClient全部可以做;HttpURLConnection没有提供的有些功能,HttpClient也提供了
Volley中两者的使用
我们知道Volley中api<=9使用的是HttpClientStack,内部使用HttpClient。api>9使用的是HurlStack,内部使用HttpUrlConnection
先来看HttpClientStack,使用起来很简单
private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) {
for (String key : headers.keySet()) {
httpRequest.setHeader(key, headers.get(key));
}
}
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
addHeaders(httpRequest, additionalHeaders); //缓存中的头
addHeaders(httpRequest, request.getHeaders()); //request中的设置的头
onPrepareRequest(httpRequest);
HttpParams httpParams = httpRequest.getParams();
int timeoutMs = request.getTimeoutMs();
// TODO: Reevaluate this connection timeout based on more wide-scale
// data collection and possibly different for wifi vs. 3G.
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
return mClient.execute(httpRequest); //调用HttpClient的excute方法,执行网络请求,最后直接返回HttpResponse
}
static HttpUriRequest createHttpRequest(Request<?> request,
Map<String, String> additionalHeaders) throws AuthFailureError {
case Method.GET:
return new HttpGet(request.getUrl());
case Method.POST: {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(postRequest, request); //设置post请求参数
return postRequest;
........
}
default:
throw new IllegalStateException("Unknown request method.");
}
private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest,
Request<?> request) throws AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
HttpEntity entity = new ByteArrayEntity(body);
httpRequest.setEntity(entity); //setEntity方法设置post请求参数
}
}
看吧,使用起来很方便简单,都是直接封装好了,,,下面来看HurlStack,使用起来就更负责了
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders()); //取出request设置的head
map.putAll(additionalHeaders); //取出缓存中的Header,如果该request前面发起过网络请求,那么就会缓存下来,同时会把http的head也缓存
........
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request); //初始化HttpURLConnection对象
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName)); //设置Http请求的head
}
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus); //HttpResponse需要自己创建
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
response.setEntity(entityFromConnection(connection)); //调用HttpResponse的setEntity方法设置相应内容
}
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h); //调用HttpResponse的addHeader方法添加响应头
}
}
return response;
}
private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {
HttpURLConnection connection = createConnection(url);
int timeoutMs = request.getTimeoutMs();
connection.setConnectTimeout(timeoutMs); //设置连接超时时间
connection.setReadTimeout(timeoutMs);
connection.setUseCaches(false); //不使用缓存?
connection.setDoInput(true); //允许输出
// use caller-provided custom SslSocketFactory, if any, for HTTPS
if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
}
return connection;
}
//可以看到volley支持get,post,PUT等都支持的,符合http规范
static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
case Method.GET:
// Not necessary to set the request method because connection defaults to GET but
// being explicit here.
connection.setRequestMethod("GET");
break;
case Method.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
...........
}
}
private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
throws IOException, AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
connection.setDoOutput(true);
connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType()); //request的getBodyContentType()方法可以设置Content-Type请求头
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body); //把getBody()返回的byte[]数组写入输入流,
out.close();
}
}
private static HttpEntity entityFromConnection(HttpURLConnection connection) {
BasicHttpEntity entity = new BasicHttpEntity();
InputStream inputStream;
try {
inputStream = connection.getInputStream();
} catch (IOException ioe) {
inputStream = connection.getErrorStream();
}
entity.setContent(inputStream); //把响应内容写入HttpEntity
entity.setContentLength(connection.getContentLength());
entity.setContentEncoding(connection.getContentEncoding());
entity.setContentType(connection.getContentType());
return entity;
}
看到两者的区别了吧?
HttpClient使用起来很简单
- mClient.execute(httpRequest); 直接返回HttpResponse对象,这个对象封装了响应内容和响应头
- httpRequest.setHeader(key, headers.get(key)); 给请求添加头
- HttpEntity entity = new ByteArrayEntity(request.getBody()); httpRequest.setEntity(entity); //给请求添加post参数
HttpURLConnection使用麻烦多了
HttpResponse对象需要自己创建,并手动添加响应内容和响应头,这里创建的是BasicHttpResponse实现了HttpResponse接口
- connection.addRequestProperty(headerName, map.get(headerName)); //设置Http请求的head
- DataOutputStream out = new DataOutputStream(connection.getOutputStream()); out.write(request.getBody()); //给请求添加post参数
- response.addHeader(h); //调用HttpResponse的addHeader方法添加响应头
- response.setEntity(entityFromConnection(connection)); //调用HttpResponse的setEntity方法设置相应内容