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


前兩篇已經對網絡請求流程已經梳理了個大概,這次我們着重看一下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);
    }

    
    
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM