前两篇已经对网络请求流程已经梳理了个大概,这次我们着重看一下HttpStack和它的其实现类。我们之前在Network篇讲过它仅有一个实现类,而今天我们讲的HttpStack有两个实现类。
其中HttpCliantStack是在2.3以下使用,Hurl是在2.3以上使用,这样分开的原因谷歌给了注释。
// 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接口
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,注释已经写的非常清楚了,如果注释有误望大家指出。
/** * 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
先看下构造,看来大框架构造都是按这格式的。
/** * 默认的构造器 * {@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.
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()方法实现。
@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。
/** * 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的一个类:
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; } }
这是测试类,访问路由器网关。
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); } }