HttpStack有两个实现类。
其中HttpCliantStack是在2.3以下使用,Hurl是在2.3以上使用,这样分开的原因谷歌给了注释。
1 2 |
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
|
2.3以下HttpUrlConnection 是不能用的,而2.3以上就是采用HttpUrlConnection 进行连接的,以下就是直接用的HttpClient。
HttpStack
先来看一下HttpStack接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public interface HttpStack {
/**
* Performs an HTTP request with the given parameters.
*通过给定的参数执行一个http请求
* <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
* and the Content-Type header is set to request.getPostBodyContentType().</p>
*
* @param request the request to perform//要执行的请求
* @param additionalHeaders additional headers to be sent together with
* {@link Request#getHeaders()}
* @return the HTTP response//执行一个请求返回一个结果
*/
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}
|
HttpCliantStack
这里区分Get和Post请求,我们先看下HttpCliantStack,注释已经写的非常清楚了,如果注释有误望大家指出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
/**
* An HttpStack that performs request over an {@link HttpClient}.
* HttpStack:通过HttpClient执行请求
* {@link Volley#newRequestQueue(android.content.Context, HttpStack)}
*/
public class HttpClientStack implements HttpStack {
protected final HttpClient mClient;//默认HttpClient
/** The Constant HEADER_CONTENT_TYPE. */
private final static String HEADER_CONTENT_TYPE = "Content-Type";
/**
* Instantiates a new http client stack.
* Volley中HttpClient可是AndroidHttpClient.newInstance(userAgent)产生的
* @param client the client
*/
public HttpClientStack(HttpClient client) {
mClient = client;
}
/**
* Adds the headers.
*
* @param httpRequest the http request
* @param headers the headers
*/
private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) {
for (String key : headers.keySet()) {
httpRequest.setHeader(key, headers.get(key));
}
}
/**
* Gets the post parameter pairs.
*
* @param postParams the post params
* @return the post parameter pairs
*/
@SuppressWarnings("unused")
private static List<NameValuePair> getPostParameterPairs(Map<String, String> postParams) {
List<NameValuePair> result = new ArrayList<NameValuePair>(postParams.size());
for (String key : postParams.keySet()) {
result.add(new BasicNameValuePair(key, postParams.get(key)));
}
return result;
}
/* (non-Javadoc)
* @see com.android.volley.toolbox.HttpStack#performRequest(com.android.volley.Request, java.util.Map)
*/
@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());//添加请求自带头部
onPrepareRequest(httpRequest);//相当于onStart,子类扩展
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.
/** 如果有更大规模的数据在Wifi和3G网络下重新评估连接超时*/
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);//设置超时
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);//设置超时
return mClient.execute(httpRequest);
}
/**
* Creates the appropriate subclass of HttpUriRequest for passed in request.
* 请求工厂类{GET,DELET,PUT,POST}
* @param request the request
* @param additionalHeaders the additional headers
* @return the http uri request
* @throws AuthFailureError the auth failure error
*/
@SuppressWarnings("deprecation")
/* protected */ static HttpUriRequest createHttpRequest(Request<?> request,
Map<String, String> additionalHeaders) throws AuthFailureError {
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST: {
// This is the deprecated way that needs to be handled for backwards compatibility.
// If the request's post body is null, then the assumption is that the request is
// GET. Otherwise, it is assumed that the request is a POST.
byte[] postBody = request.getPostBody();
if (postBody != null) {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
HttpEntity entity;
entity = new ByteArrayEntity(postBody);
postRequest.setEntity(entity);
return postRequest;
} else {
return new HttpGet(request.getUrl());
}
}
case Method.GET:
return new HttpGet(request.getUrl());
case Method.DELETE:
return new HttpDelete(request.getUrl());
case Method.POST: {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(postRequest, request);
return postRequest;
}
case Method.PUT: {
HttpPut putRequest = new HttpPut(request.getUrl());
putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(putRequest, request);
return putRequest;
}
default:
throw new IllegalStateException("Unknown request method.");
}
}
/**
* Sets the entity if non empty body.
* 非空体Entity
* @param httpRequest the http request
* @param request the request
* @throws AuthFailureError the auth failure error
*/
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);
}
}
/**
* Called before the request is executed using the underlying HttpClient.
* 在请求之前调用
* <p>Overwrite in subclasses to augment the request.</p>
* 由子类覆写扩展
* @param request the request
* @throws IOException Signals that an I/O exception has occurred.
*/
protected void onPrepareRequest(HttpUriRequest request) throws IOException {
// Nothing.
}
}
|
它的构造参数是一个HttpClient,Velloy是用AndroidHttpClient.newInstance(userAgent)建立一个HttpClient实例。
再看这个类的最重要方法也就是对HttpStack接口的performRequest()方法进行具体实现。
第一步:通过这个静态方法createHttpRequest()来获取一个HttpUriRequest请求实例,其中HttpGet,HttpPost,HttpPut,HttpDelete都实现了HttpUriRequest这个接口。
这步就确定了请求类型和创立了Http实际请求。
第二步:通过这两个方法添加Http头部,
addHeaders(httpRequest, additionalHeaders);//添加自定义头部
addHeaders(httpRequest, request.getHeaders());//添加请求自带头部
第三步:这个方法
onPrepareRequest()
挺人性化的,相当于AsyncTask的onPreExecute(),不过要实现这个方法需要自行扩展此类,谷歌没有把她提出来。
一开始我还以为没有类似的方法,像Afinal的方法名字取得很吸引人,叫onStart(),onSuccess(),onFailure()。其实在Volley与之相对应的都有,onResponse就相当于onSuccess(),onErrorResponse就相当于onFailure(),而onPrepareRequest()就对应onStart()。
第四步:设置超时,这个超时设置值是在Request里封装进去了,Request封装了很多东西,比如请求的URL等。
在此谷歌在超时这里做了很温馨的提示,设置连接超时考虑WIFI和3G网络设置不一样,这里应该可以留出一个缺口,根据实际网络设置不一样的时常。貌似现在很多应用会根据网络来进行内容排版,例如什么无图模式啊,仅在wifi下上传等。
HurlStack
先看下构造,看来大框架构造都是按这格式的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/**
* 默认的构造器
* {@link Volley#newRequestQueue(android.content.Context, HttpStack)}
*/
public HurlStack() {
this(null);
}
/**
* @param urlRewriter Rewriter to use for request URLs
*/
public HurlStack(UrlRewriter urlRewriter) {
this(urlRewriter, null);
}
/**
* 两个主要参数
* @param urlRewriter Rewriter to use for request URLs//Url转换器
* @param sslSocketFactory SSL factory to use for HTTPS connections//安全连接
*/
public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
mUrlRewriter = urlRewriter;
mSslSocketFactory = sslSocketFactory;
}
|
由此可见主要字段就是mUrlRewriter 和mSslSocketFactory 。前面的是URL转换接口,后面是安全连接。
这个URL转换接口还是挺人性化的,可以过滤些非法字符和省略Http或者www.
1 2 3 4 5 6 7 |
public interface UrlRewriter {
/**
* Returns a URL to use instead of the provided one, or null to indicate
* this URL should not be used at all.
*/
public String rewriteUrl(String originalUrl);
}
|
然后我们还是看HttpStack接口的performRequest()方法实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();//获取这个请求的url
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
if (mUrlRewriter != null) {//默认的不会对url转换
String rewritten = mUrlRewriter.rewriteUrl(url);//实现UrlRewriter#rewriteUrl方法
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
URL parsedUrl = new URL(url);//解析后Url
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {//为connection添加属性
connection.addRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);//设置请求方法
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {//不能取回ResponseCode
// -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);//通过responseStatus获得一个BasicHttpResponse
response.setEntity(entityFromConnection(connection));//设置Entity
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {//header
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}
|
第一步:获取请求地址url,如果写了UrlRewriter就会按照这个接口的规则更改URL。
第二步:建立URL,通过createConnection()方法获得一个HttpURLConnection,通过openConnection()方法设置一些超时类的Http配置,然后添加头部,并且通过setConnectionParametersForRequest()方法设置HttpURLConnection的请求方法(PUT.GET,POST,DELETE...)。
第三步:然后通过HttpURLConnection获取ResponseCode,ResponseMessage,ResponseStatus,BasicHttpResponse响应结果,并进行响应处理。
第四步:通过entityFromConnection(),把HttpURLConnection获得的流转化为HttpEntity,并返回带HttpEntity的HttpResponse。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/**
* Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
* 从HttpURLConnection获取一个HttpEntity
* @param connection
* @return an HttpEntity populated with data from <code>connection</code>.
*/
private static HttpEntity entityFromConnection(HttpURLConnection connection) {
BasicHttpEntity entity = new BasicHttpEntity();
InputStream inputStream;//首先从HttpURLConnection获取一个输入流
try {
inputStream = connection.getInputStream();
} catch (IOException ioe) {
inputStream = connection.getErrorStream();
}
entity.setContent(inputStream);//把流设置为HttpEntity的Content
entity.setContentLength(connection.getContentLength());//设置内容长度
entity.setContentEncoding(connection.getContentEncoding());//设置编码格式
entity.setContentType(connection.getContentType());//设置内容类型
return entity;
}
|
为什么HurlStack没有onPrepareRequest()方法,如果要的话那就只有知己加了。
来看扩展HttpClientStack的一个类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class IpadHttpStack extends HttpClientStack{
interface OnStartListener{
void onStart(HttpUriRequest request);
}
OnStartListener mOnStartListener;
static String ua = "ipad";
public IpadHttpStack() {
super(AndroidHttpClient.newInstance(ua));
// TODO Auto-generated constructor stub
}
@Override
protected void onPrepareRequest(HttpUriRequest request) throws IOException {
// TODO Auto-generated method stub
super.onPrepareRequest(request);
if(mOnStartListener!=null)
mOnStartListener.onStart(request);
}
public void setOnStartListener(OnStartListener listener) {
this.mOnStartListener = listener;
}
}
|
这是测试类,访问路由器网关。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
public class MainActivity extends Activity {
RequestQueue mQueue;
IpadHttpStack bvinHttp;
private static HashSet<Class<?>> exeptionList = new HashSet<Class<?>>();
static{
exeptionList.add(AuthFailureError.class);
exeptionList.add(NetworkError.class);
exeptionList.add(NoConnectionError.class);
exeptionList.add(ParseError.class);
exeptionList.add(ServerError.class);
exeptionList.add(TimeoutError.class);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.e("onResponse","sdgdsg");
bvinHttp = new IpadHttpStack();
mQueue = Volley.newRequestQueue(getApplicationContext(),bvinHttp);
//StringRequest四个构造参数分别是Request类型,url,网络请求响应监、听,器,错误监,听,器
bvinHttp.setOnStartListener(new IpadHttpStack.OnStartListener() {
@Override
public void onStart(HttpUriRequest request) {
// TODO Auto-generated method stub
}
});
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
// TODO Auto-generated method stub
return new PasswordAuthentication("admin", "admin".toCharArray());
}
});
mQueue.add(new StringRequest(Method.GET, "http://192.168.1.1", new Listener<String>(){
@Override
public void onResponse(String arg0) {
// TODO Auto-generated method stub
Log.e("onResponse", arg0);
}
}, new ErrorListener(){
@Override
public void onErrorResponse(VolleyError arg0) {
// TODO Auto-generated method stub
if (arg0 instanceof TimeoutError) {
Log.e("onErrorResponse", "超时");
}else if(arg0 instanceof AuthFailureError){
Log.e("AuthFailureError", arg0.toString());
}
Log.e("AuthFailureError", arg0.toString());
//exeptionList.contains(arg0)
}
}));
mQueue.start();
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
mQueue.stop();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
menu.add("取消");
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
mQueue.cancelAll(new RequestFilter(){
@Override
public boolean apply(Request<?> arg0) {
// TODO Auto-generated method stub
arg0.cancel();
return false;
}});
return super.onOptionsItemSelected(item);
}
}
|