谷歌Volley網絡框架講解——HttpStack及其實現類

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



}
發佈了48 篇原創文章 · 獲贊 3 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章