谷歌Volley网络框架讲解——HttpStack及其实现类

前两篇已经对网络请求流程已经梳理了个大概,这次我们着重看一下HttpStack和它的其实现类。我们之前在Network篇讲过它仅有一个实现类,而今天我们讲的HttpStack有两个实现类。

谷歌Volley网络框架讲解——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);

    }



    

    

}

 

你可能感兴趣的:(Volley)