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

本文详细介绍了Android网络库Volley中的HTTP请求处理机制,包括HttpStack接口及其两种实现:HttpClientStack与HurlStack。深入探讨了这两种实现的区别及各自的适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
    }



}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值